FIX: Several fixes on the FV function
(1+x)^(1+y) was stringifyfied incorrectly We still need work on this FV now returns currency FV(-1,-2,1) should return #DIV/0! not #NUM!
This commit is contained in:
committed by
Nicolás Hatcher Andrés
parent
8c6aaf2af0
commit
23814ec18c
@@ -49,18 +49,7 @@ pub mod stringify;
|
|||||||
pub mod walk;
|
pub mod walk;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod tests;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_ranges;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_move_formula;
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_tables;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_issue_155;
|
|
||||||
|
|
||||||
pub(crate) fn parse_range(formula: &str) -> Result<(i32, i32, i32, i32), String> {
|
pub(crate) fn parse_range(formula: &str) -> Result<(i32, i32, i32, i32), String> {
|
||||||
let mut lexer = lexer::Lexer::new(
|
let mut lexer = lexer::Lexer::new(
|
||||||
|
|||||||
@@ -456,11 +456,65 @@ fn stringify(
|
|||||||
};
|
};
|
||||||
format!("{}{}{}", x, kind, y)
|
format!("{}{}{}", x, kind, y)
|
||||||
}
|
}
|
||||||
OpPowerKind { left, right } => format!(
|
OpPowerKind { left, right } => {
|
||||||
"{}^{}",
|
let x = match **left {
|
||||||
stringify(left, context, displace_data, use_original_name),
|
BooleanKind(_)
|
||||||
|
| NumberKind(_)
|
||||||
|
| StringKind(_)
|
||||||
|
| ReferenceKind { .. }
|
||||||
|
| RangeKind { .. }
|
||||||
|
| WrongReferenceKind { .. }
|
||||||
|
| VariableKind(_)
|
||||||
|
| WrongRangeKind { .. } => {
|
||||||
|
stringify(left, context, displace_data, use_original_name)
|
||||||
|
}
|
||||||
|
OpRangeKind { .. }
|
||||||
|
| OpConcatenateKind { .. }
|
||||||
|
| OpProductKind { .. }
|
||||||
|
| OpPowerKind { .. }
|
||||||
|
| FunctionKind { .. }
|
||||||
|
| InvalidFunctionKind { .. }
|
||||||
|
| ArrayKind(_)
|
||||||
|
| UnaryKind { .. }
|
||||||
|
| ErrorKind(_)
|
||||||
|
| ParseErrorKind { .. }
|
||||||
|
| OpSumKind { .. }
|
||||||
|
| CompareKind { .. }
|
||||||
|
| EmptyArgKind => format!(
|
||||||
|
"({})",
|
||||||
|
stringify(left, context, displace_data, use_original_name)
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let y = match **right {
|
||||||
|
BooleanKind(_)
|
||||||
|
| NumberKind(_)
|
||||||
|
| StringKind(_)
|
||||||
|
| ReferenceKind { .. }
|
||||||
|
| RangeKind { .. }
|
||||||
|
| WrongReferenceKind { .. }
|
||||||
|
| VariableKind(_)
|
||||||
|
| WrongRangeKind { .. } => {
|
||||||
|
stringify(right, context, displace_data, use_original_name)
|
||||||
|
}
|
||||||
|
OpRangeKind { .. }
|
||||||
|
| OpConcatenateKind { .. }
|
||||||
|
| OpProductKind { .. }
|
||||||
|
| OpPowerKind { .. }
|
||||||
|
| FunctionKind { .. }
|
||||||
|
| InvalidFunctionKind { .. }
|
||||||
|
| ArrayKind(_)
|
||||||
|
| UnaryKind { .. }
|
||||||
|
| ErrorKind(_)
|
||||||
|
| ParseErrorKind { .. }
|
||||||
|
| OpSumKind { .. }
|
||||||
|
| CompareKind { .. }
|
||||||
|
| EmptyArgKind => format!(
|
||||||
|
"({})",
|
||||||
stringify(right, context, displace_data, use_original_name)
|
stringify(right, context, displace_data, use_original_name)
|
||||||
),
|
),
|
||||||
|
};
|
||||||
|
format!("{}^{}", x, y)
|
||||||
|
}
|
||||||
InvalidFunctionKind { name, args } => {
|
InvalidFunctionKind { name, args } => {
|
||||||
format_function(name, args, context, displace_data, use_original_name)
|
format_function(name, args, context, displace_data, use_original_name)
|
||||||
}
|
}
|
||||||
|
|||||||
6
base/src/expressions/parser/tests/mod.rs
Normal file
6
base/src/expressions/parser/tests/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
mod test_general;
|
||||||
|
mod test_issue_155;
|
||||||
|
mod test_move_formula;
|
||||||
|
mod test_ranges;
|
||||||
|
mod test_stringify;
|
||||||
|
mod test_tables;
|
||||||
@@ -3,17 +3,11 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::expressions::lexer::LexerMode;
|
use crate::expressions::lexer::LexerMode;
|
||||||
use crate::expressions::parser::stringify::DisplaceData;
|
use crate::expressions::parser::stringify::{
|
||||||
|
to_rc_format, to_string, to_string_displaced, DisplaceData,
|
||||||
use super::super::types::CellReferenceRC;
|
|
||||||
use super::Parser;
|
|
||||||
use super::{
|
|
||||||
super::parser::{
|
|
||||||
stringify::{to_rc_format, to_string},
|
|
||||||
Node,
|
|
||||||
},
|
|
||||||
stringify::to_string_displaced,
|
|
||||||
};
|
};
|
||||||
|
use crate::expressions::parser::{Node, Parser};
|
||||||
|
use crate::expressions::types::CellReferenceRC;
|
||||||
|
|
||||||
struct Formula<'a> {
|
struct Formula<'a> {
|
||||||
initial: &'a str,
|
initial: &'a str,
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::super::parser::stringify::to_string;
|
use crate::expressions::parser::stringify::to_string;
|
||||||
use super::super::types::CellReferenceRC;
|
use crate::expressions::parser::Parser;
|
||||||
use super::Parser;
|
use crate::expressions::types::CellReferenceRC;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn issue_155_parser() {
|
fn issue_155_parser() {
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::expressions::parser::move_formula::{move_formula, MoveContext};
|
use crate::expressions::parser::move_formula::{move_formula, MoveContext};
|
||||||
use crate::expressions::types::Area;
|
use crate::expressions::parser::Parser;
|
||||||
|
use crate::expressions::types::{Area, CellReferenceRC};
|
||||||
use super::super::types::CellReferenceRC;
|
|
||||||
use super::Parser;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_move_formula() {
|
fn test_move_formula() {
|
||||||
@@ -2,9 +2,9 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use crate::expressions::lexer::LexerMode;
|
use crate::expressions::lexer::LexerMode;
|
||||||
|
|
||||||
use super::super::parser::stringify::{to_rc_format, to_string};
|
use crate::expressions::parser::stringify::{to_rc_format, to_string};
|
||||||
use super::super::types::CellReferenceRC;
|
use crate::expressions::parser::Parser;
|
||||||
use super::Parser;
|
use crate::expressions::types::CellReferenceRC;
|
||||||
|
|
||||||
struct Formula<'a> {
|
struct Formula<'a> {
|
||||||
formula_a1: &'a str,
|
formula_a1: &'a str,
|
||||||
34
base/src/expressions/parser/tests/test_stringify.rs
Normal file
34
base/src/expressions/parser/tests/test_stringify.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#![allow(clippy::panic)]
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::expressions::parser::stringify::to_string;
|
||||||
|
use crate::expressions::parser::Parser;
|
||||||
|
use crate::expressions::types::CellReferenceRC;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exp_order() {
|
||||||
|
let worksheets = vec!["Sheet1".to_string()];
|
||||||
|
let mut parser = Parser::new(worksheets, HashMap::new());
|
||||||
|
|
||||||
|
// Reference cell is Sheet1!A1
|
||||||
|
let cell_reference = CellReferenceRC {
|
||||||
|
sheet: "Sheet1".to_string(),
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
};
|
||||||
|
let t = parser.parse("(1 + 2)^3 + 4", &Some(cell_reference.clone()));
|
||||||
|
assert_eq!(to_string(&t, &cell_reference), "(1+2)^3+4");
|
||||||
|
|
||||||
|
let t = parser.parse("(C5 + 3)^R4", &Some(cell_reference.clone()));
|
||||||
|
assert_eq!(to_string(&t, &cell_reference), "(C5+3)^R4");
|
||||||
|
|
||||||
|
let t = parser.parse("(C5 + 3)^(R4*6)", &Some(cell_reference.clone()));
|
||||||
|
assert_eq!(to_string(&t, &cell_reference), "(C5+3)^(R4*6)");
|
||||||
|
|
||||||
|
let t = parser.parse("(C5)^(R4)", &Some(cell_reference.clone()));
|
||||||
|
assert_eq!(to_string(&t, &cell_reference), "C5^R4");
|
||||||
|
|
||||||
|
let t = parser.parse("(5)^(4)", &Some(cell_reference.clone()));
|
||||||
|
assert_eq!(to_string(&t, &cell_reference), "5^4");
|
||||||
|
}
|
||||||
@@ -6,8 +6,8 @@ use crate::expressions::parser::stringify::to_string;
|
|||||||
use crate::expressions::utils::{number_to_column, parse_reference_a1};
|
use crate::expressions::utils::{number_to_column, parse_reference_a1};
|
||||||
use crate::types::{Table, TableColumn, TableStyleInfo};
|
use crate::types::{Table, TableColumn, TableStyleInfo};
|
||||||
|
|
||||||
use super::super::types::CellReferenceRC;
|
use crate::expressions::parser::Parser;
|
||||||
use super::Parser;
|
use crate::expressions::types::CellReferenceRC;
|
||||||
|
|
||||||
fn create_test_table(
|
fn create_test_table(
|
||||||
table_name: &str,
|
table_name: &str,
|
||||||
@@ -89,6 +89,9 @@ fn compute_future_value(
|
|||||||
if rate == 0.0 {
|
if rate == 0.0 {
|
||||||
return Ok(-pv - pmt * nper);
|
return Ok(-pv - pmt * nper);
|
||||||
}
|
}
|
||||||
|
if rate == -1.0 && nper < 0.0 {
|
||||||
|
return Err((Error::DIV, "Divide by zero".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
let rate_nper = (1.0 + rate).powf(nper);
|
let rate_nper = (1.0 + rate).powf(nper);
|
||||||
let fv = if period_start {
|
let fv = if period_start {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ mod test_number_format;
|
|||||||
|
|
||||||
mod test_escape_quotes;
|
mod test_escape_quotes;
|
||||||
mod test_extend;
|
mod test_extend;
|
||||||
|
mod test_fn_fv;
|
||||||
mod test_fn_type;
|
mod test_fn_type;
|
||||||
mod test_frozen_rows_and_columns;
|
mod test_frozen_rows_and_columns;
|
||||||
mod test_geomean;
|
mod test_geomean;
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
use crate::test::util::new_empty_model;
|
use crate::test::util::new_empty_model;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_cases() {}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wrong_number_of_arguments() {
|
fn wrong_number_of_arguments() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
|
|||||||
36
base/src/test/test_fn_fv.rs
Normal file
36
base/src/test/test_fn_fv.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::test::util::new_empty_model;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn computation() {
|
||||||
|
let i2 = "=-C2*(1+D2)^E2-F2*((D2+1)*((1+D2)^E2-1))/D2";
|
||||||
|
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model._set("C2", "1");
|
||||||
|
model._set("D2", "2");
|
||||||
|
model._set("E2", "3");
|
||||||
|
model._set("F2", "4");
|
||||||
|
|
||||||
|
model._set("I2", i2);
|
||||||
|
|
||||||
|
model.evaluate();
|
||||||
|
|
||||||
|
assert_eq!(model._get_text("I2"), "-183");
|
||||||
|
assert_eq!(model._get_formula("I2"), i2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_as_currency() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model._set("C2", "1");
|
||||||
|
model._set("D2", "2");
|
||||||
|
model._set("E2", "3");
|
||||||
|
model._set("F2", "4");
|
||||||
|
|
||||||
|
model._set("I2", "=FV(D2,E2,F2,C2,1)");
|
||||||
|
|
||||||
|
model.evaluate();
|
||||||
|
|
||||||
|
assert_eq!(model._get_text("I2"), "-$183.00");
|
||||||
|
}
|
||||||
@@ -309,6 +309,7 @@ impl Model {
|
|||||||
Function::Sum => self.units_fn_sum_like(args, cell),
|
Function::Sum => self.units_fn_sum_like(args, cell),
|
||||||
Function::Average => self.units_fn_sum_like(args, cell),
|
Function::Average => self.units_fn_sum_like(args, cell),
|
||||||
Function::Pmt => self.units_fn_currency(args, cell),
|
Function::Pmt => self.units_fn_currency(args, cell),
|
||||||
|
Function::Fv => self.units_fn_currency(args, cell),
|
||||||
Function::Nper => self.units_fn_currency(args, cell),
|
Function::Nper => self.units_fn_currency(args, cell),
|
||||||
Function::Npv => self.units_fn_currency(args, cell),
|
Function::Npv => self.units_fn_currency(args, cell),
|
||||||
Function::Irr => self.units_fn_percentage(args, cell),
|
Function::Irr => self.units_fn_percentage(args, cell),
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user