UPDATE: Dump of initial files
This commit is contained in:
321
base/src/functions/logical.rs
Normal file
321
base/src/functions/logical.rs
Normal file
@@ -0,0 +1,321 @@
|
||||
use crate::{
|
||||
calc_result::{CalcResult, CellReference},
|
||||
expressions::parser::Node,
|
||||
expressions::token::Error,
|
||||
model::Model,
|
||||
};
|
||||
|
||||
use super::util::compare_values;
|
||||
|
||||
impl Model {
|
||||
pub(crate) fn fn_if(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
|
||||
if args.len() == 2 || args.len() == 3 {
|
||||
let cond_result = self.get_boolean(&args[0], cell);
|
||||
let cond = match cond_result {
|
||||
Ok(f) => f,
|
||||
Err(s) => {
|
||||
return s;
|
||||
}
|
||||
};
|
||||
if cond {
|
||||
return self.evaluate_node_in_context(&args[1], cell);
|
||||
} else if args.len() == 3 {
|
||||
return self.evaluate_node_in_context(&args[2], cell);
|
||||
} else {
|
||||
return CalcResult::Boolean(false);
|
||||
}
|
||||
}
|
||||
CalcResult::new_args_number_error(cell)
|
||||
}
|
||||
|
||||
pub(crate) fn fn_iferror(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
|
||||
if args.len() == 2 {
|
||||
let value = self.evaluate_node_in_context(&args[0], cell);
|
||||
match value {
|
||||
CalcResult::Error { .. } => {
|
||||
return self.evaluate_node_in_context(&args[1], cell);
|
||||
}
|
||||
_ => return value,
|
||||
}
|
||||
}
|
||||
CalcResult::new_args_number_error(cell)
|
||||
}
|
||||
|
||||
pub(crate) fn fn_ifna(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
|
||||
if args.len() == 2 {
|
||||
let value = self.evaluate_node_in_context(&args[0], cell);
|
||||
if let CalcResult::Error { error, .. } = &value {
|
||||
if error == &Error::NA {
|
||||
return self.evaluate_node_in_context(&args[1], cell);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
CalcResult::new_args_number_error(cell)
|
||||
}
|
||||
|
||||
pub(crate) fn fn_not(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
|
||||
if args.len() == 1 {
|
||||
match self.get_boolean(&args[0], cell) {
|
||||
Ok(f) => return CalcResult::Boolean(!f),
|
||||
Err(s) => {
|
||||
return s;
|
||||
}
|
||||
};
|
||||
}
|
||||
CalcResult::new_args_number_error(cell)
|
||||
}
|
||||
|
||||
pub(crate) fn fn_and(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
|
||||
let mut true_count = 0;
|
||||
for arg in args {
|
||||
match self.evaluate_node_in_context(arg, cell) {
|
||||
CalcResult::Boolean(b) => {
|
||||
if !b {
|
||||
return CalcResult::Boolean(false);
|
||||
}
|
||||
true_count += 1;
|
||||
}
|
||||
CalcResult::Number(value) => {
|
||||
if value == 0.0 {
|
||||
return CalcResult::Boolean(false);
|
||||
}
|
||||
true_count += 1;
|
||||
}
|
||||
CalcResult::String(_value) => {
|
||||
true_count += 1;
|
||||
}
|
||||
CalcResult::Range { left, right } => {
|
||||
if left.sheet != right.sheet {
|
||||
return CalcResult::new_error(
|
||||
Error::VALUE,
|
||||
cell,
|
||||
"Ranges are in different sheets".to_string(),
|
||||
);
|
||||
}
|
||||
for row in left.row..(right.row + 1) {
|
||||
for column in left.column..(right.column + 1) {
|
||||
match self.evaluate_cell(CellReference {
|
||||
sheet: left.sheet,
|
||||
row,
|
||||
column,
|
||||
}) {
|
||||
CalcResult::Boolean(b) => {
|
||||
if !b {
|
||||
return CalcResult::Boolean(false);
|
||||
}
|
||||
true_count += 1;
|
||||
}
|
||||
CalcResult::Number(value) => {
|
||||
if value == 0.0 {
|
||||
return CalcResult::Boolean(false);
|
||||
}
|
||||
true_count += 1;
|
||||
}
|
||||
CalcResult::String(_value) => {
|
||||
true_count += 1;
|
||||
}
|
||||
error @ CalcResult::Error { .. } => return error,
|
||||
CalcResult::Range { .. } => {}
|
||||
CalcResult::EmptyCell | CalcResult::EmptyArg => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
error @ CalcResult::Error { .. } => return error,
|
||||
CalcResult::EmptyCell | CalcResult::EmptyArg => {}
|
||||
};
|
||||
}
|
||||
if true_count == 0 {
|
||||
return CalcResult::new_error(
|
||||
Error::VALUE,
|
||||
cell,
|
||||
"Boolean values not found".to_string(),
|
||||
);
|
||||
}
|
||||
CalcResult::Boolean(true)
|
||||
}
|
||||
|
||||
pub(crate) fn fn_or(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
|
||||
let mut result = false;
|
||||
for arg in args {
|
||||
match self.evaluate_node_in_context(arg, cell) {
|
||||
CalcResult::Boolean(value) => result = value || result,
|
||||
CalcResult::Number(value) => {
|
||||
if value != 0.0 {
|
||||
return CalcResult::Boolean(true);
|
||||
}
|
||||
}
|
||||
CalcResult::String(_value) => {
|
||||
return CalcResult::Boolean(true);
|
||||
}
|
||||
CalcResult::Range { left, right } => {
|
||||
if left.sheet != right.sheet {
|
||||
return CalcResult::new_error(
|
||||
Error::VALUE,
|
||||
cell,
|
||||
"Ranges are in different sheets".to_string(),
|
||||
);
|
||||
}
|
||||
for row in left.row..(right.row + 1) {
|
||||
for column in left.column..(right.column + 1) {
|
||||
match self.evaluate_cell(CellReference {
|
||||
sheet: left.sheet,
|
||||
row,
|
||||
column,
|
||||
}) {
|
||||
CalcResult::Boolean(value) => {
|
||||
result = value || result;
|
||||
}
|
||||
CalcResult::Number(value) => {
|
||||
if value != 0.0 {
|
||||
return CalcResult::Boolean(true);
|
||||
}
|
||||
}
|
||||
CalcResult::String(_value) => {
|
||||
return CalcResult::Boolean(true);
|
||||
}
|
||||
error @ CalcResult::Error { .. } => return error,
|
||||
CalcResult::Range { .. } => {}
|
||||
CalcResult::EmptyCell | CalcResult::EmptyArg => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
error @ CalcResult::Error { .. } => return error,
|
||||
CalcResult::EmptyCell | CalcResult::EmptyArg => {}
|
||||
};
|
||||
}
|
||||
CalcResult::Boolean(result)
|
||||
}
|
||||
|
||||
/// XOR(logical1, [logical]*,...)
|
||||
/// Logical1 is required, subsequent logical values are optional. Can be logical values, arrays, or references.
|
||||
/// The result of XOR is TRUE when the number of TRUE inputs is odd and FALSE when the number of TRUE inputs is even.
|
||||
pub(crate) fn fn_xor(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
|
||||
let mut true_count = 0;
|
||||
let mut false_count = 0;
|
||||
for arg in args {
|
||||
match self.evaluate_node_in_context(arg, cell) {
|
||||
CalcResult::Boolean(b) => {
|
||||
if b {
|
||||
true_count += 1;
|
||||
} else {
|
||||
false_count += 1;
|
||||
}
|
||||
}
|
||||
CalcResult::Number(value) => {
|
||||
if value != 0.0 {
|
||||
true_count += 1;
|
||||
} else {
|
||||
false_count += 1;
|
||||
}
|
||||
}
|
||||
CalcResult::Range { left, right } => {
|
||||
if left.sheet != right.sheet {
|
||||
return CalcResult::new_error(
|
||||
Error::VALUE,
|
||||
cell,
|
||||
"Ranges are in different sheets".to_string(),
|
||||
);
|
||||
}
|
||||
for row in left.row..(right.row + 1) {
|
||||
for column in left.column..(right.column + 1) {
|
||||
match self.evaluate_cell(CellReference {
|
||||
sheet: left.sheet,
|
||||
row,
|
||||
column,
|
||||
}) {
|
||||
CalcResult::Boolean(b) => {
|
||||
if b {
|
||||
true_count += 1;
|
||||
} else {
|
||||
false_count += 1;
|
||||
}
|
||||
}
|
||||
CalcResult::Number(value) => {
|
||||
if value != 0.0 {
|
||||
true_count += 1;
|
||||
} else {
|
||||
false_count += 1;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
if true_count == 0 && false_count == 0 {
|
||||
return CalcResult::new_error(Error::VALUE, cell, "No booleans found".to_string());
|
||||
}
|
||||
CalcResult::Boolean(true_count % 2 == 1)
|
||||
}
|
||||
|
||||
/// =SWITCH(expression, case1, value1, [case, value]*, [default])
|
||||
pub(crate) fn fn_switch(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
|
||||
let args_count = args.len();
|
||||
if args_count < 3 {
|
||||
return CalcResult::new_args_number_error(cell);
|
||||
}
|
||||
// TODO add implicit intersection
|
||||
let expr = self.evaluate_node_in_context(&args[0], cell);
|
||||
if expr.is_error() {
|
||||
return expr;
|
||||
}
|
||||
|
||||
// How many cases we have?
|
||||
// 3, 4 args -> 1 case
|
||||
let case_count = (args_count - 1) / 2;
|
||||
for case_index in 0..case_count {
|
||||
let case = self.evaluate_node_in_context(&args[2 * case_index + 1], cell);
|
||||
if case.is_error() {
|
||||
return case;
|
||||
}
|
||||
if compare_values(&expr, &case) == 0 {
|
||||
return self.evaluate_node_in_context(&args[2 * case_index + 2], cell);
|
||||
}
|
||||
}
|
||||
// None of the cases matched so we return the default
|
||||
// If there is an even number of args is the last one otherwise is #N/A
|
||||
if args_count % 2 == 0 {
|
||||
return self.evaluate_node_in_context(&args[args_count - 1], cell);
|
||||
}
|
||||
CalcResult::Error {
|
||||
error: Error::NA,
|
||||
origin: cell,
|
||||
message: "Did not find a match".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// =IFS(condition1, value, [condition, value]*)
|
||||
pub(crate) fn fn_ifs(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
|
||||
let args_count = args.len();
|
||||
if args_count < 2 {
|
||||
return CalcResult::new_args_number_error(cell);
|
||||
}
|
||||
if args_count % 2 != 0 {
|
||||
// Missing value for last condition
|
||||
return CalcResult::new_args_number_error(cell);
|
||||
}
|
||||
let case_count = args_count / 2;
|
||||
for case_index in 0..case_count {
|
||||
let value = self.get_boolean(&args[2 * case_index], cell);
|
||||
match value {
|
||||
Ok(b) => {
|
||||
if b {
|
||||
return self.evaluate_node_in_context(&args[2 * case_index + 1], cell);
|
||||
}
|
||||
}
|
||||
Err(s) => return s,
|
||||
}
|
||||
}
|
||||
CalcResult::Error {
|
||||
error: Error::NA,
|
||||
origin: cell,
|
||||
message: "Did not find a match".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user