use crate::builtins::generated::extendedbigdecimal::ExtendedBigDecimal;
use bigdecimal::BigDecimal;
use bigdecimal::num_bigint::BigInt;
use bigdecimal::num_bigint::BigUint;
use bigdecimal::num_bigint::Sign;
use num_traits::Signed;
use num_traits::ToPrimitive;
use num_traits::Zero;
#[derive(Clone, Copy, PartialEq)]
enum Base {
Binary = 2,
Octal = 8,
Decimal = 10,
Hexadecimal = 16,
}
impl Base {
fn digit(self, c: char) -> Option<u64> {
fn from_decimal(c: char) -> u64 {
u64::from(c) - u64::from('0')
}
match self {
Self::Binary => ('0'..='1').contains(&c).then(|| from_decimal(c)),
Self::Octal => ('0'..='7').contains(&c).then(|| from_decimal(c)),
Self::Decimal => c.is_ascii_digit().then(|| from_decimal(c)),
Self::Hexadecimal => match c.to_ascii_lowercase() {
'0'..='9' => Some(from_decimal(c)),
c @ 'a'..='f' => Some(u64::from(c) - u64::from('a') + 10),
_ => None,
},
}
}
fn parse_digits(self, str: &str) -> (Option<BigUint>, &str) {
let (digits, _, rest) = self.parse_digits_count(str, None);
(digits, rest)
}
fn parse_digits_count(
self,
str: &str,
digits: Option<BigUint>,
) -> (Option<BigUint>, i64, &str) {
let mut digits: Option<BigUint> = digits;
let mut count: i64 = 0;
let mut rest = str;
let mut digits_tmp: u64 = 0;
let mut count_tmp: i64 = 0;
let mut mul_tmp: u64 = 1;
while let Some(d) = rest.chars().next().and_then(|c| self.digit(c)) {
(digits_tmp, count_tmp, mul_tmp) = (
digits_tmp * self as u64 + d,
count_tmp + 1,
mul_tmp * self as u64,
);
rest = &rest[1..];
if count_tmp >= 15 {
(digits, count) = (
Some(digits.unwrap_or_default() * mul_tmp + digits_tmp),
count + count_tmp,
);
(digits_tmp, count_tmp, mul_tmp) = (0, 0, 1);
}
}
if mul_tmp > 1 {
(digits, count) = (
Some(digits.unwrap_or_default() * mul_tmp + digits_tmp),
count + count_tmp,
);
}
(digits, count, rest)
}
}
#[derive(Debug, PartialEq)]
pub enum ExtendedParserError<T> {
NotNumeric,
PartialMatch(T, String),
Overflow(T),
Underflow(T),
}
impl<T> ExtendedParserError<T>
where
T: Zero,
{
fn extract(self) -> T {
match self {
Self::NotNumeric => T::zero(),
Self::PartialMatch(v, _) => v,
Self::Overflow(v) => v,
Self::Underflow(v) => v,
}
}
fn map<U>(
self,
f: impl FnOnce(T) -> Result<U, ExtendedParserError<U>>,
) -> ExtendedParserError<U>
where
U: Zero,
{
fn extract<U>(v: Result<U, ExtendedParserError<U>>) -> U
where
U: Zero,
{
v.unwrap_or_else(ExtendedParserError::extract)
}
match self {
Self::NotNumeric => ExtendedParserError::NotNumeric,
Self::PartialMatch(v, rest) => ExtendedParserError::PartialMatch(extract(f(v)), rest),
Self::Overflow(v) => ExtendedParserError::Overflow(extract(f(v))),
Self::Underflow(v) => ExtendedParserError::Underflow(extract(f(v))),
}
}
}
pub trait ExtendedParser {
fn extended_parse(input: &str) -> Result<Self, ExtendedParserError<Self>>
where
Self: Sized;
}
impl ExtendedParser for i64 {
fn extended_parse(input: &str) -> Result<Self, ExtendedParserError<Self>> {
fn into_i64(ebd: ExtendedBigDecimal) -> Result<i64, ExtendedParserError<i64>> {
match ebd {
ExtendedBigDecimal::BigDecimal(bd) => {
let (digits, scale) = bd.into_bigint_and_scale();
if scale == 0 {
let negative = digits.sign() == Sign::Minus;
match i64::try_from(digits) {
Ok(i) => Ok(i),
_ => Err(ExtendedParserError::Overflow(if negative {
i64::MIN
} else {
i64::MAX
})),
}
} else {
Err(ExtendedParserError::NotNumeric)
}
}
ExtendedBigDecimal::MinusZero => Ok(0),
_ => Err(ExtendedParserError::NotNumeric),
}
}
match parse(input, ParseTarget::Integral, &[]) {
Ok(v) => into_i64(v),
Err(e) => Err(e.map(into_i64)),
}
}
}
impl ExtendedParser for u64 {
fn extended_parse(input: &str) -> Result<Self, ExtendedParserError<Self>> {
fn into_u64(ebd: ExtendedBigDecimal) -> Result<u64, ExtendedParserError<u64>> {
match ebd {
ExtendedBigDecimal::BigDecimal(bd) => {
let (digits, scale) = bd.into_bigint_and_scale();
if scale == 0 {
let (sign, digits) = digits.into_parts();
match u64::try_from(digits) {
Ok(i) => {
if sign == Sign::Minus {
Ok(!i + 1)
} else {
Ok(i)
}
}
_ => Err(ExtendedParserError::Overflow(u64::MAX)),
}
} else {
Err(ExtendedParserError::NotNumeric)
}
}
ExtendedBigDecimal::MinusZero => Ok(0),
_ => Err(ExtendedParserError::NotNumeric),
}
}
match parse(input, ParseTarget::Integral, &[]) {
Ok(v) => into_u64(v),
Err(e) => Err(e.map(into_u64)),
}
}
}
impl ExtendedParser for f64 {
fn extended_parse(input: &str) -> Result<Self, ExtendedParserError<Self>> {
fn into_f64(ebd: ExtendedBigDecimal) -> Result<f64, ExtendedParserError<f64>> {
let v = match ebd {
ExtendedBigDecimal::BigDecimal(bd) => {
let f = bd.to_f64().unwrap();
if f.is_infinite() {
return Err(ExtendedParserError::Overflow(f));
}
if f.is_zero() && !bd.is_zero() {
return Err(ExtendedParserError::Underflow(f));
}
f
}
ExtendedBigDecimal::MinusZero => -0.0,
ExtendedBigDecimal::Nan => f64::NAN,
ExtendedBigDecimal::MinusNan => -f64::NAN,
ExtendedBigDecimal::Infinity => f64::INFINITY,
ExtendedBigDecimal::MinusInfinity => -f64::INFINITY,
};
Ok(v)
}
match parse(input, ParseTarget::Decimal, &[]) {
Ok(v) => into_f64(v),
Err(e) => Err(e.map(into_f64)),
}
}
}
impl ExtendedParser for ExtendedBigDecimal {
fn extended_parse(input: &str) -> Result<Self, ExtendedParserError<Self>> {
parse(input, ParseTarget::Decimal, &[])
}
}
fn parse_digits(base: Base, str: &str, fractional: bool) -> (Option<BigUint>, i64, &str) {
let (digits, rest) = base.parse_digits(str);
if fractional {
if let Some(rest) = rest.strip_prefix('.') {
return base.parse_digits_count(rest, digits);
}
}
(digits, 0, rest)
}
fn parse_exponent(base: Base, str: &str) -> (Option<BigInt>, &str) {
let exp_chars = match base {
Base::Decimal => ['e', 'E'],
Base::Hexadecimal => ['p', 'P'],
_ => unreachable!(),
};
if let Some(rest) = str.strip_prefix(exp_chars) {
let (sign, rest) = if let Some(rest) = rest.strip_prefix('-') {
(Sign::Minus, rest)
} else if let Some(rest) = rest.strip_prefix('+') {
(Sign::Plus, rest)
} else {
(Sign::Plus, rest)
};
let (exp_uint, rest) = Base::Decimal.parse_digits(rest);
if let Some(exp_uint) = exp_uint {
return (Some(BigInt::from_biguint(sign, exp_uint)), rest);
}
}
(None, str)
}
fn parse_suffix_multiplier<'a>(str: &'a str, allowed_suffixes: &[(char, u32)]) -> (u32, &'a str) {
if let Some(ch) = str.chars().next() {
if let Some(mul) = allowed_suffixes
.iter()
.find_map(|(c, t)| (ch == *c).then_some(*t))
{
return (mul, &str[1..]);
}
}
(1, str)
}
fn parse_special_value(
input: &str,
negative: bool,
allowed_suffixes: &[(char, u32)],
) -> Result<ExtendedBigDecimal, ExtendedParserError<ExtendedBigDecimal>> {
const MATCH_TABLE: &[(&str, ExtendedBigDecimal)] = &[
("infinity", ExtendedBigDecimal::Infinity),
("inf", ExtendedBigDecimal::Infinity),
("nan", ExtendedBigDecimal::Nan),
];
let input_lc = input.to_ascii_lowercase();
for (str, ebd) in MATCH_TABLE {
if input_lc.starts_with(str) {
let mut special = ebd.clone();
if negative {
special = -special;
}
let (_, rest) = parse_suffix_multiplier(&input[str.len()..], allowed_suffixes);
return if rest.is_empty() {
Ok(special)
} else {
Err(ExtendedParserError::PartialMatch(special, rest.to_string()))
};
}
}
Err(ExtendedParserError::NotNumeric)
}
fn make_error(overflow: bool, negative: bool) -> ExtendedParserError<ExtendedBigDecimal> {
let mut v = if overflow {
ExtendedBigDecimal::Infinity
} else {
ExtendedBigDecimal::zero()
};
if negative {
v = -v;
}
if overflow {
ExtendedParserError::Overflow(v)
} else {
ExtendedParserError::Underflow(v)
}
}
fn construct_extended_big_decimal(
digits: BigUint,
negative: bool,
base: Base,
scale: i64,
exponent: BigInt,
) -> Result<ExtendedBigDecimal, ExtendedParserError<ExtendedBigDecimal>> {
if digits == BigUint::zero() {
return Ok(if negative {
ExtendedBigDecimal::MinusZero
} else {
ExtendedBigDecimal::zero()
});
}
let sign = if negative { Sign::Minus } else { Sign::Plus };
let signed_digits = BigInt::from_biguint(sign, digits);
let bd = if scale == 0 && exponent.is_zero() {
BigDecimal::from_bigint(signed_digits, 0)
} else if base == Base::Decimal {
if exponent.is_zero() {
BigDecimal::from_bigint(signed_digits, scale)
} else {
let new_scale = -exponent + scale;
if let Some(new_scale) = new_scale.to_i64() {
BigDecimal::from_bigint(signed_digits, new_scale)
} else {
return Err(make_error(new_scale.is_negative(), negative));
}
}
} else if base == Base::Hexadecimal {
if scale > u32::MAX.into() {
return Err(ExtendedParserError::NotNumeric);
}
let bd = BigDecimal::from_bigint(signed_digits, 0)
/ BigDecimal::from_bigint(BigInt::from(16).pow(scale as u32), 0);
let Some(exponent) = exponent.to_i64() else {
return Err(make_error(exponent.is_positive(), negative));
};
let base: BigDecimal = 2.into();
let pow2 = base.powi(exponent);
bd * pow2
} else {
unreachable!();
};
Ok(ExtendedBigDecimal::BigDecimal(bd))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ParseTarget {
Decimal,
Integral,
Duration,
}
pub(crate) fn parse(
input: &str,
target: ParseTarget,
allowed_suffixes: &[(char, u32)],
) -> Result<ExtendedBigDecimal, ExtendedParserError<ExtendedBigDecimal>> {
let trimmed_input = input.trim_ascii_start();
let (negative, unsigned) = if let Some(trimmed_input) = trimmed_input.strip_prefix('-') {
(true, trimmed_input)
} else if let Some(trimmed_input) = trimmed_input.strip_prefix('+') {
(false, trimmed_input)
} else {
(false, trimmed_input)
};
let (base, rest) = if let Some(rest) = unsigned.strip_prefix('0') {
if let Some(rest) = rest.strip_prefix(['x', 'X']) {
(Base::Hexadecimal, rest)
} else if target == ParseTarget::Integral {
if let Some(rest) = rest.strip_prefix(['b', 'B']) {
(Base::Binary, rest)
} else {
(Base::Octal, unsigned)
}
} else {
(Base::Decimal, unsigned)
}
} else {
(Base::Decimal, unsigned)
};
let parse_frac_exp =
matches!(base, Base::Decimal | Base::Hexadecimal) && target != ParseTarget::Integral;
let (digits, scale, rest) = parse_digits(base, rest, parse_frac_exp);
let (exponent, rest) = if parse_frac_exp {
parse_exponent(base, rest)
} else {
(None, rest)
};
if digits.is_none() {
if let Some(partial) = unsigned.strip_prefix("0") {
let ebd = if negative {
ExtendedBigDecimal::MinusZero
} else {
ExtendedBigDecimal::zero()
};
return Err(ExtendedParserError::PartialMatch(ebd, partial.to_string()));
}
return if target == ParseTarget::Integral {
Err(ExtendedParserError::NotNumeric)
} else {
parse_special_value(unsigned, negative, allowed_suffixes)
};
}
let (mul, rest) = parse_suffix_multiplier(rest, allowed_suffixes);
let digits = digits.unwrap() * mul;
let ebd_result =
construct_extended_big_decimal(digits, negative, base, scale, exponent.unwrap_or_default());
if rest.is_empty() {
ebd_result
} else {
Err(ExtendedParserError::PartialMatch(
ebd_result.unwrap_or_else(ExtendedParserError::extract),
rest.to_string(),
))
}
}