UPDATE: Implements BASE and DECIMAL (#504)

This commit is contained in:
Nicolás Hatcher Andrés
2025-11-02 23:30:43 +01:00
committed by GitHub
parent 8b7fdce278
commit 7f57826371
4 changed files with 129 additions and 18 deletions

View File

@@ -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),
} }
} }

View File

@@ -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);

View File

@@ -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),
} }
} }
} }

Binary file not shown.