UPDATE: Implements BASE and DECIMAL (#504)
This commit is contained in:
committed by
GitHub
parent
8b7fdce278
commit
7f57826371
@@ -864,6 +864,8 @@ fn get_function_args_signature(kind: &Function, arg_count: usize) -> Vec<Signatu
|
|||||||
Function::Trunc => args_signature_scalars(arg_count, 1, 1),
|
Function::Trunc => args_signature_scalars(arg_count, 1, 1),
|
||||||
Function::Gcd => vec![Signature::Vector; arg_count],
|
Function::Gcd => vec![Signature::Vector; arg_count],
|
||||||
Function::Lcm => vec![Signature::Vector; arg_count],
|
Function::Lcm => vec![Signature::Vector; arg_count],
|
||||||
|
Function::Base => args_signature_scalars(arg_count, 2, 1),
|
||||||
|
Function::Decimal => args_signature_scalars(arg_count, 2, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1116,5 +1118,7 @@ fn static_analysis_on_function(kind: &Function, args: &[Node]) -> StaticResult {
|
|||||||
Function::Trunc => scalar_arguments(args),
|
Function::Trunc => scalar_arguments(args),
|
||||||
Function::Gcd => not_implemented(args),
|
Function::Gcd => not_implemented(args),
|
||||||
Function::Lcm => not_implemented(args),
|
Function::Lcm => not_implemented(args),
|
||||||
|
Function::Base => scalar_arguments(args),
|
||||||
|
Function::Decimal => scalar_arguments(args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,6 +133,118 @@ impl Model {
|
|||||||
CalcResult::Number(result)
|
CalcResult::Number(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fn_base(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
|
let arg_count = args.len();
|
||||||
|
if !(2..=3).contains(&arg_count) {
|
||||||
|
return CalcResult::new_args_number_error(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
// number to convert
|
||||||
|
let mut value = match self.get_number(&args[0], cell) {
|
||||||
|
Ok(f) => f.trunc() as i64,
|
||||||
|
Err(s) => return s,
|
||||||
|
};
|
||||||
|
// radix
|
||||||
|
let radix = match self.get_number(&args[1], cell) {
|
||||||
|
Ok(f) => f.trunc() as i64,
|
||||||
|
Err(s) => return s,
|
||||||
|
};
|
||||||
|
// optional min_length
|
||||||
|
let min_length = if arg_count == 3 {
|
||||||
|
match self.get_number(&args[2], cell) {
|
||||||
|
Ok(f) => {
|
||||||
|
if f < 0.0 {
|
||||||
|
return CalcResult::Error {
|
||||||
|
error: Error::NUM,
|
||||||
|
origin: cell,
|
||||||
|
message: "Minimum length must be non-negative".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
f.trunc() as usize
|
||||||
|
}
|
||||||
|
Err(s) => return s,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
if !(2..=36).contains(&radix) {
|
||||||
|
return CalcResult::Error {
|
||||||
|
error: Error::NUM,
|
||||||
|
origin: cell,
|
||||||
|
message: "Radix must be between 2 and 36".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// number must be >= 0
|
||||||
|
if value < 0 {
|
||||||
|
return CalcResult::Error {
|
||||||
|
error: Error::NUM,
|
||||||
|
origin: cell,
|
||||||
|
message: "Number must be non-negative".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = String::new();
|
||||||
|
if value == 0 {
|
||||||
|
buf.push('0');
|
||||||
|
} else {
|
||||||
|
while value > 0 {
|
||||||
|
let digit = (value % radix) as u8;
|
||||||
|
let ch = match digit {
|
||||||
|
0..=9 => (b'0' + digit) as char,
|
||||||
|
10..=35 => (b'A' + (digit - 10)) as char,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
buf.push(ch);
|
||||||
|
value /= radix;
|
||||||
|
}
|
||||||
|
// we built it in reverse
|
||||||
|
buf = buf.chars().rev().collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// pad with leading zeros if needed
|
||||||
|
if buf.len() < min_length {
|
||||||
|
let mut padded = String::with_capacity(min_length);
|
||||||
|
for _ in 0..(min_length - buf.len()) {
|
||||||
|
padded.push('0');
|
||||||
|
}
|
||||||
|
padded.push_str(&buf);
|
||||||
|
buf = padded;
|
||||||
|
}
|
||||||
|
|
||||||
|
CalcResult::String(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fn_decimal(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
|
if args.len() != 2 {
|
||||||
|
return CalcResult::new_args_number_error(cell);
|
||||||
|
}
|
||||||
|
let text = match self.get_string(&args[0], cell) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(s) => return s,
|
||||||
|
};
|
||||||
|
let radix = match self.get_number(&args[1], cell) {
|
||||||
|
Ok(f) => f.trunc() as i32,
|
||||||
|
Err(s) => return s,
|
||||||
|
};
|
||||||
|
if !(2..=36).contains(&radix) {
|
||||||
|
return CalcResult::Error {
|
||||||
|
error: Error::NUM,
|
||||||
|
origin: cell,
|
||||||
|
message: "Radix must be between 2 and 36".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
match i64::from_str_radix(&text, radix as u32) {
|
||||||
|
Ok(n) => CalcResult::Number(n as f64),
|
||||||
|
Err(_) => CalcResult::Error {
|
||||||
|
error: Error::VALUE,
|
||||||
|
origin: cell,
|
||||||
|
message: format!("'{}' is not a valid number in base {}", text, radix),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn fn_gcd(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
pub(crate) fn fn_gcd(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return CalcResult::new_args_number_error(cell);
|
return CalcResult::new_args_number_error(cell);
|
||||||
|
|||||||
@@ -84,15 +84,12 @@ pub enum Function {
|
|||||||
Csch,
|
Csch,
|
||||||
Sec,
|
Sec,
|
||||||
Sech,
|
Sech,
|
||||||
|
|
||||||
Exp,
|
Exp,
|
||||||
Fact,
|
Fact,
|
||||||
Factdouble,
|
Factdouble,
|
||||||
Sign,
|
Sign,
|
||||||
|
|
||||||
Radians,
|
Radians,
|
||||||
Degrees,
|
Degrees,
|
||||||
|
|
||||||
Int,
|
Int,
|
||||||
Even,
|
Even,
|
||||||
Odd,
|
Odd,
|
||||||
@@ -107,9 +104,10 @@ pub enum Function {
|
|||||||
Quotient,
|
Quotient,
|
||||||
Mround,
|
Mround,
|
||||||
Trunc,
|
Trunc,
|
||||||
|
|
||||||
Gcd,
|
Gcd,
|
||||||
Lcm,
|
Lcm,
|
||||||
|
Base,
|
||||||
|
Decimal,
|
||||||
|
|
||||||
// Information
|
// Information
|
||||||
ErrorType,
|
ErrorType,
|
||||||
@@ -304,7 +302,7 @@ pub enum Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn into_iter() -> IntoIter<Function, 245> {
|
pub fn into_iter() -> IntoIter<Function, 247> {
|
||||||
[
|
[
|
||||||
Function::And,
|
Function::And,
|
||||||
Function::False,
|
Function::False,
|
||||||
@@ -366,6 +364,8 @@ impl Function {
|
|||||||
Function::Trunc,
|
Function::Trunc,
|
||||||
Function::Gcd,
|
Function::Gcd,
|
||||||
Function::Lcm,
|
Function::Lcm,
|
||||||
|
Function::Base,
|
||||||
|
Function::Decimal,
|
||||||
Function::Max,
|
Function::Max,
|
||||||
Function::Min,
|
Function::Min,
|
||||||
Function::Product,
|
Function::Product,
|
||||||
@@ -593,13 +593,14 @@ impl Function {
|
|||||||
Function::Sheet => "_xlfn.SHEET".to_string(),
|
Function::Sheet => "_xlfn.SHEET".to_string(),
|
||||||
Function::Formulatext => "_xlfn.FORMULATEXT".to_string(),
|
Function::Formulatext => "_xlfn.FORMULATEXT".to_string(),
|
||||||
Function::Isoweeknum => "_xlfn.ISOWEEKNUM".to_string(),
|
Function::Isoweeknum => "_xlfn.ISOWEEKNUM".to_string(),
|
||||||
|
|
||||||
Function::Ceiling => "_xlfn.CEILING".to_string(),
|
Function::Ceiling => "_xlfn.CEILING".to_string(),
|
||||||
Function::CeilingMath => "_xlfn.CEILING.MATH".to_string(),
|
Function::CeilingMath => "_xlfn.CEILING.MATH".to_string(),
|
||||||
Function::CeilingPrecise => "_xlfn.CEILING.PRECISE".to_string(),
|
Function::CeilingPrecise => "_xlfn.CEILING.PRECISE".to_string(),
|
||||||
Function::FloorMath => "_xlfn.FLOOR.MATH".to_string(),
|
Function::FloorMath => "_xlfn.FLOOR.MATH".to_string(),
|
||||||
Function::FloorPrecise => "_xlfn.FLOOR.PRECISE".to_string(),
|
Function::FloorPrecise => "_xlfn.FLOOR.PRECISE".to_string(),
|
||||||
Function::IsoCeiling => "_xlfn.ISO.CEILING".to_string(),
|
Function::IsoCeiling => "_xlfn.ISO.CEILING".to_string(),
|
||||||
|
Function::Base => "_xlfn.BASE".to_string(),
|
||||||
|
Function::Decimal => "_xlfn.DECIMAL".to_string(),
|
||||||
|
|
||||||
_ => self.to_string(),
|
_ => self.to_string(),
|
||||||
}
|
}
|
||||||
@@ -623,23 +624,18 @@ impl Function {
|
|||||||
"SWITCH" | "_XLFN.SWITCH" => Some(Function::Switch),
|
"SWITCH" | "_XLFN.SWITCH" => Some(Function::Switch),
|
||||||
"TRUE" => Some(Function::True),
|
"TRUE" => Some(Function::True),
|
||||||
"XOR" | "_XLFN.XOR" => Some(Function::Xor),
|
"XOR" | "_XLFN.XOR" => Some(Function::Xor),
|
||||||
|
|
||||||
"SIN" => Some(Function::Sin),
|
"SIN" => Some(Function::Sin),
|
||||||
"COS" => Some(Function::Cos),
|
"COS" => Some(Function::Cos),
|
||||||
"TAN" => Some(Function::Tan),
|
"TAN" => Some(Function::Tan),
|
||||||
|
|
||||||
"ASIN" => Some(Function::Asin),
|
"ASIN" => Some(Function::Asin),
|
||||||
"ACOS" => Some(Function::Acos),
|
"ACOS" => Some(Function::Acos),
|
||||||
"ATAN" => Some(Function::Atan),
|
"ATAN" => Some(Function::Atan),
|
||||||
|
|
||||||
"SINH" => Some(Function::Sinh),
|
"SINH" => Some(Function::Sinh),
|
||||||
"COSH" => Some(Function::Cosh),
|
"COSH" => Some(Function::Cosh),
|
||||||
"TANH" => Some(Function::Tanh),
|
"TANH" => Some(Function::Tanh),
|
||||||
|
|
||||||
"ASINH" => Some(Function::Asinh),
|
"ASINH" => Some(Function::Asinh),
|
||||||
"ACOSH" => Some(Function::Acosh),
|
"ACOSH" => Some(Function::Acosh),
|
||||||
"ATANH" => Some(Function::Atanh),
|
"ATANH" => Some(Function::Atanh),
|
||||||
|
|
||||||
"ACOT" => Some(Function::Acot),
|
"ACOT" => Some(Function::Acot),
|
||||||
"COTH" => Some(Function::Coth),
|
"COTH" => Some(Function::Coth),
|
||||||
"COT" => Some(Function::Cot),
|
"COT" => Some(Function::Cot),
|
||||||
@@ -648,15 +644,12 @@ impl Function {
|
|||||||
"SEC" => Some(Function::Sec),
|
"SEC" => Some(Function::Sec),
|
||||||
"SECH" => Some(Function::Sech),
|
"SECH" => Some(Function::Sech),
|
||||||
"ACOTH" => Some(Function::Acoth),
|
"ACOTH" => Some(Function::Acoth),
|
||||||
|
|
||||||
"FACT" => Some(Function::Fact),
|
"FACT" => Some(Function::Fact),
|
||||||
"FACTDOUBLE" => Some(Function::Factdouble),
|
"FACTDOUBLE" => Some(Function::Factdouble),
|
||||||
"EXP" => Some(Function::Exp),
|
"EXP" => Some(Function::Exp),
|
||||||
"SIGN" => Some(Function::Sign),
|
"SIGN" => Some(Function::Sign),
|
||||||
|
|
||||||
"RADIANS" => Some(Function::Radians),
|
"RADIANS" => Some(Function::Radians),
|
||||||
"DEGREES" => Some(Function::Degrees),
|
"DEGREES" => Some(Function::Degrees),
|
||||||
|
|
||||||
"INT" => Some(Function::Int),
|
"INT" => Some(Function::Int),
|
||||||
"EVEN" => Some(Function::Even),
|
"EVEN" => Some(Function::Even),
|
||||||
"ODD" => Some(Function::Odd),
|
"ODD" => Some(Function::Odd),
|
||||||
@@ -671,21 +664,19 @@ impl Function {
|
|||||||
"QUOTIENT" => Some(Function::Quotient),
|
"QUOTIENT" => Some(Function::Quotient),
|
||||||
"MROUND" => Some(Function::Mround),
|
"MROUND" => Some(Function::Mround),
|
||||||
"TRUNC" => Some(Function::Trunc),
|
"TRUNC" => Some(Function::Trunc),
|
||||||
|
|
||||||
"GCD" => Some(Function::Gcd),
|
"GCD" => Some(Function::Gcd),
|
||||||
"LCM" => Some(Function::Lcm),
|
"LCM" => Some(Function::Lcm),
|
||||||
|
"BASE" | "_XLFN.BASE" => Some(Function::Base),
|
||||||
|
"DECIMAL" | "_XLFN.DECIMAL" => Some(Function::Decimal),
|
||||||
"PI" => Some(Function::Pi),
|
"PI" => Some(Function::Pi),
|
||||||
"ABS" => Some(Function::Abs),
|
"ABS" => Some(Function::Abs),
|
||||||
"SQRT" => Some(Function::Sqrt),
|
"SQRT" => Some(Function::Sqrt),
|
||||||
"SQRTPI" => Some(Function::Sqrtpi),
|
"SQRTPI" => Some(Function::Sqrtpi),
|
||||||
"POWER" => Some(Function::Power),
|
"POWER" => Some(Function::Power),
|
||||||
"ATAN2" => Some(Function::Atan2),
|
"ATAN2" => Some(Function::Atan2),
|
||||||
|
|
||||||
"LN" => Some(Function::Ln),
|
"LN" => Some(Function::Ln),
|
||||||
"LOG" => Some(Function::Log),
|
"LOG" => Some(Function::Log),
|
||||||
"LOG10" => Some(Function::Log10),
|
"LOG10" => Some(Function::Log10),
|
||||||
|
|
||||||
"MAX" => Some(Function::Max),
|
"MAX" => Some(Function::Max),
|
||||||
"MIN" => Some(Function::Min),
|
"MIN" => Some(Function::Min),
|
||||||
"PRODUCT" => Some(Function::Product),
|
"PRODUCT" => Some(Function::Product),
|
||||||
@@ -1138,6 +1129,8 @@ impl fmt::Display for Function {
|
|||||||
Function::Trunc => write!(f, "TRUNC"),
|
Function::Trunc => write!(f, "TRUNC"),
|
||||||
Function::Gcd => write!(f, "GCD"),
|
Function::Gcd => write!(f, "GCD"),
|
||||||
Function::Lcm => write!(f, "LCM"),
|
Function::Lcm => write!(f, "LCM"),
|
||||||
|
Function::Base => write!(f, "BASE"),
|
||||||
|
Function::Decimal => write!(f, "DECIMAL"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1411,6 +1404,8 @@ impl Model {
|
|||||||
Function::Trunc => self.fn_trunc(args, cell),
|
Function::Trunc => self.fn_trunc(args, cell),
|
||||||
Function::Gcd => self.fn_gcd(args, cell),
|
Function::Gcd => self.fn_gcd(args, cell),
|
||||||
Function::Lcm => self.fn_lcm(args, cell),
|
Function::Lcm => self.fn_lcm(args, cell),
|
||||||
|
Function::Base => self.fn_base(args, cell),
|
||||||
|
Function::Decimal => self.fn_decimal(args, cell),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
xlsx/tests/calc_tests/BASE.xlsx
Normal file
BIN
xlsx/tests/calc_tests/BASE.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user