use crate::{
errors::{syntax_error, ParseResult},
ParserCore,
};
use super::NumberParserSettings;
#[inline]
fn exponent_adjust(value: u64) -> i32 {
64i32 - (value.leading_zeros() as i32)
}
pub fn parse_octal_float(
parser: &mut ParserCore,
negative: bool,
settings: &NumberParserSettings,
) -> ParseResult<f64> {
#[derive(Debug)]
enum State {
None,
Digit,
One,
Decimal,
DigitDecimal,
OneDecimal,
}
let mut state = State::None;
let mut mantissa = 0u64;
let mut exponent = 0i32;
let mut whole_empty = false;
let mut fraction_empty = false;
let loc = parser.loc();
while !parser.is_at_eof() {
let ch = parser.peek();
if settings.permit_underscores && ch == '_' {
parser.consume();
continue;
}
match state {
State::None => match ch {
'0' => {
state = State::Digit;
}
'1'..='7' => {
mantissa = ch as u64 - '0' as u64;
exponent = exponent_adjust(mantissa) - 1;
state = State::One;
}
'.' => {
whole_empty = true;
fraction_empty = true;
state = State::Decimal;
}
ch if ch.is_alphanumeric() => {
return Err(syntax_error(
parser.loc(),
format!("Invalid octal digit '{}'", ch).as_str(),
));
}
_ => {
break;
}
},
State::Digit => match ch {
'0' => {}
'1'..='7' => {
mantissa = ch as u64 - '0' as u64;
exponent = exponent_adjust(mantissa) - 1;
state = State::One;
}
'.' => {
fraction_empty = true;
state = State::DigitDecimal;
}
'e' | 'E' | 'p' | 'P' => {
break;
}
ch if ch.is_alphanumeric() => {
return Err(syntax_error(
parser.loc(),
format!("Invalid octal digit '{}'", ch).as_str(),
));
}
_ => {
break;
}
},
State::One => match ch {
'0' => {
if mantissa >= 0x2000_0000_0000_0000 {
return Err(syntax_error(parser.loc(), "Too many significant digits"));
}
mantissa <<= 3;
exponent += 3;
}
'1'..='7' => {
if mantissa >= 0x2000_0000_0000_0000 {
return Err(syntax_error(parser.loc(), "Too many significant digits"));
}
mantissa <<= 3;
mantissa |= ch as u64 - '0' as u64;
exponent += 3;
}
'.' => {
fraction_empty = true;
state = State::OneDecimal;
}
'e' | 'E' | 'p' | 'P' => {
break;
}
ch if ch.is_alphanumeric() => {
return Err(syntax_error(
parser.loc(),
format!("Invalid octal digit '{}'", ch).as_str(),
));
}
_ => {
break;
}
},
State::Decimal => match ch {
'0' => {
exponent -= 3;
state = State::DigitDecimal;
fraction_empty = false;
}
'1'..='7' => {
mantissa = ch as u64 - '0' as u64;
exponent = exponent_adjust(mantissa) - 4;
state = State::OneDecimal;
fraction_empty = false;
}
'e' | 'E' | 'p' | 'P' => {
break;
}
ch if ch.is_alphanumeric() => {
return Err(syntax_error(
parser.loc(),
format!("Invalid octal digit '{}'", ch).as_str(),
));
}
_ => {
break;
}
},
State::DigitDecimal => match ch {
'0' => {
exponent -= 3;
fraction_empty = false;
}
'1'..='7' => {
mantissa = ch as u64 - '0' as u64;
exponent += exponent_adjust(mantissa) - 4;
state = State::OneDecimal;
fraction_empty = false;
}
'e' | 'E' | 'p' | 'P' => {
break;
}
ch if ch.is_alphanumeric() => {
return Err(syntax_error(
parser.loc(),
format!("Invalid octal digit '{}'", ch).as_str(),
));
}
_ => {
break;
}
},
State::OneDecimal => match ch {
'0' => {
if mantissa >= 0x2000_0000_0000_0000 {
return Err(syntax_error(parser.loc(), "Too many significant digits"));
}
mantissa <<= 3;
}
'1'..='7' => {
if mantissa >= 0x2000_0000_0000_0000 {
return Err(syntax_error(parser.loc(), "Too many significant digits"));
}
mantissa <<= 3;
mantissa |= ch as u64 - '0' as u64;
}
'e' | 'E' | 'p' | 'P' => {
break;
}
ch if ch.is_alphanumeric() => {
return Err(syntax_error(
parser.loc(),
format!("Invalid octal digit '{}'", ch).as_str(),
));
}
_ => {
break;
}
},
}
parser.consume();
}
let ch = parser.peek();
if ch == 'e' || ch == 'E' || ch == 'p' || ch == 'P' {
parser.consume();
let loc = parser.loc();
let negexp = if parser.peek_and_consume('-') {
true
} else {
parser.peek_and_consume('+');
false
};
let digits = if settings.permit_underscores {
parser.take_while_unless(|ch| ch.is_ascii_digit(), |ch| ch == '_')
} else {
parser.take_while(|ch| ch.is_ascii_digit())
};
let power = match digits.parse::<i32>() {
Ok(value) => value,
Err(msg) => return Err(syntax_error(loc, &msg.to_string())),
};
if negexp {
exponent -= power * 3;
} else {
exponent += power * 3;
}
}
match state {
State::None | State::Decimal => {
return Err(syntax_error(
parser.loc(),
"Expected a number but did not find one",
));
}
_ => {}
}
if whole_empty && !settings.permit_empty_whole {
return Err(syntax_error(
loc,
"An empty whole part is not permitted; there must be digits to the left of the decimal."
));
}
if fraction_empty && !settings.permit_empty_fraction {
return Err(syntax_error(
loc,
"An empty fraction part is not permitted; there must be digits to the right of the decimal, if present."
));
}
match state {
State::One | State::OneDecimal => {}
_ => return Ok(0.0),
}
if !(f64::MIN_EXP - 1..f64::MAX_EXP).contains(&exponent) {
return Err(syntax_error(parser.loc(), "Exponent is out of range"));
}
let mut bits = ((exponent + 1023) as u64) << 52;
let leading_zeros = mantissa.leading_zeros();
match leading_zeros.cmp(&11) {
std::cmp::Ordering::Less => {
mantissa >>= 11 - leading_zeros;
}
std::cmp::Ordering::Greater => {
mantissa <<= leading_zeros - 11;
}
_ => {}
}
mantissa &= 0x000f_ffff_ffff_ffff;
bits |= mantissa;
if negative {
bits |= 0x8000_0000_0000_0000;
}
Ok(f64::from_bits(bits))
}
#[cfg(test)]
mod test {
use crate::{
decoder::Decode,
numbers::{octal_float::parse_octal_float, NumberParserSettings},
ParserCore,
};
fn parse(value: &str) -> ParserCore {
let decoder = Decode::from_string(value);
ParserCore::new("<string>>", decoder)
}
#[test]
fn zero_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_octal_float(&mut parse(""), false, &settings).is_err());
assert_eq!(
parse_octal_float(&mut parse("0"), false, &settings).unwrap(),
0.0
);
assert_eq!(
parse_octal_float(&mut parse("0"), true, &settings).unwrap(),
0.0
);
}
#[test]
fn one_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_octal_float(&mut parse("1"), false, &settings).unwrap(),
1.0
);
assert_eq!(
parse_octal_float(&mut parse("1"), true, &settings).unwrap(),
-1.0
);
assert_eq!(
parse_octal_float(&mut parse("01"), false, &settings).unwrap(),
1.0
);
assert_eq!(
parse_octal_float(&mut parse("0000000001"), true, &settings).unwrap(),
-1.0
);
}
#[test]
fn decimal_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_octal_float(&mut parse(".0"), true, &settings).unwrap(),
0.0
);
assert_eq!(
parse_octal_float(&mut parse("0."), true, &settings).unwrap(),
0.0
);
assert_eq!(
parse_octal_float(&mut parse("1."), true, &settings).unwrap(),
-1.0
);
assert_eq!(
parse_octal_float(&mut parse(".1"), true, &settings).unwrap(),
-1.0 / 8.0
);
}
#[test]
fn integer_test_1() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_octal_float(&mut parse("17"), false, &settings).unwrap(),
15.0
);
assert_eq!(
parse_octal_float(&mut parse("10"), true, &settings).unwrap(),
-8.0
);
assert_eq!(
parse_octal_float(&mut parse("100744"), true, &settings).unwrap(),
-33252.0
);
assert_eq!(
parse_octal_float(&mut parse("020440"), true, &settings).unwrap(),
-8480.0
);
}
#[test]
fn integer_test_2() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_octal_float(&mut parse("157"), false, &settings).unwrap(),
111.0
);
settings.permit_underscores = true;
assert_eq!(
parse_octal_float(&mut parse("44210"), false, &settings).unwrap(),
18568.0
);
settings.permit_underscores = false;
assert_eq!(
parse_octal_float(&mut parse("313777714623131463"), true, &settings).unwrap(),
-7177605032489779.0
);
settings.permit_underscores = true;
assert_eq!(
parse_octal_float(&mut parse("1_231"), false, &settings).unwrap(),
665.0
);
}
#[test]
fn fraction_test_1() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_octal_float(&mut parse("0.1"), false, &settings).unwrap(),
1.0 / 8.0
);
assert_eq!(
parse_octal_float(&mut parse("0.02"), false, &settings).unwrap(),
2.0 / 8.0 / 8.0
);
assert_eq!(
parse_octal_float(&mut parse("0.4"), false, &settings).unwrap(),
0.5
);
assert_eq!(
parse_octal_float(&mut parse("1.4"), false, &settings).unwrap(),
1.5
);
}
#[test]
fn fraction_test_2() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_octal_float(&mut parse("0.04"), false, &settings).unwrap(),
4.0 / 8.0 / 8.0
);
assert_eq!(
parse_octal_float(&mut parse("20.004"), false, &settings).unwrap(),
16.0078125
);
assert_eq!(
parse_octal_float(&mut parse("16.16"), false, &settings).unwrap(),
14.21875
);
assert_eq!(
parse_octal_float(&mut parse("000.0000"), false, &settings).unwrap(),
0.0
);
}
#[test]
fn exponent_test_1() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_octal_float(&mut parse("0p1000"), false, &settings).unwrap(),
0.0
);
assert_eq!(
parse_octal_float(&mut parse("10e1"), false, &settings).unwrap(),
64.0
);
assert_eq!(
parse_octal_float(&mut parse("10P-1"), false, &settings).unwrap(),
1.0
);
assert_eq!(
parse_octal_float(&mut parse("0.001E3"), false, &settings).unwrap(),
1.0
);
}
#[test]
fn exponent_test_2() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_octal_float(&mut parse("1021e-3"), false, &settings).unwrap(),
1.033203125
);
assert_eq!(
parse_octal_float(&mut parse("44e-3"), false, &settings).unwrap(),
0.0703125
);
}
#[test]
fn limits_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = true;
assert_eq!(
parse_octal_float(&mut parse("1.7_77777_77777_77777_74p341"), false, &settings)
.unwrap(),
f64::MAX
);
assert_eq!(
parse_octal_float(&mut parse("1.7_77777_77777_77777_74p341"), true, &settings).unwrap(),
f64::MIN
);
assert_eq!(
parse_octal_float(&mut parse("2p-341"), false, &settings).unwrap(),
f64::MIN_POSITIVE
);
}
#[test]
fn overflow_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_octal_float(&mut parse("2p341"), false, &settings).is_err());
assert!(parse_octal_float(&mut parse("1p-341"), false, &settings).is_err());
settings.permit_underscores = true;
assert_eq!(
parse_octal_float(
&mut parse("1_777_777_777_777_777_777_777"),
false,
&settings
)
.unwrap(),
1.844674407370955e19f64
);
assert!(parse_octal_float(
&mut parse("3_777_777_777_777_777_777_777"),
false,
&settings
)
.is_err());
}
#[test]
fn reject_test_1() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_octal_float(&mut parse("g"), false, &settings).is_err());
assert!(parse_octal_float(&mut parse(","), false, &settings).is_err());
}
#[test]
fn reject_test_2() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_octal_float(&mut parse("0g"), false, &settings).is_err());
assert_eq!(
parse_octal_float(&mut parse("0,"), false, &settings).unwrap(),
0.0
);
assert!(parse_octal_float(&mut parse("1g"), false, &settings).is_err());
assert_eq!(
parse_octal_float(&mut parse("1,"), false, &settings).unwrap(),
1.0
);
}
#[test]
fn reject_test_3() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_octal_float(&mut parse("0.g"), false, &settings).is_err());
assert_eq!(
parse_octal_float(&mut parse("0.,"), false, &settings).unwrap(),
0.0
);
assert!(parse_octal_float(&mut parse("1.g"), false, &settings).is_err());
assert_eq!(
parse_octal_float(&mut parse("1.,"), false, &settings).unwrap(),
1.0
);
}
#[test]
fn reject_test_4() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_octal_float(&mut parse("0.0g"), false, &settings).is_err());
assert!(parse_octal_float(&mut parse("1.0g"), false, &settings).is_err());
assert!(parse_octal_float(&mut parse(".p12"), false, &settings).is_err());
}
#[test]
fn reject_test_5() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_octal_float(&mut parse(".g"), false, &settings).is_err());
assert!(parse_octal_float(&mut parse(".,"), false, &settings).is_err());
settings.permit_underscores = true;
assert!(parse_octal_float(&mut parse("0.p4294967296"), false, &settings).is_err());
}
#[test]
fn reject_test_6() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = true;
assert!(
parse_octal_float(&mut parse("0.17_7777777_7777777_7777777"), false, &settings)
.is_err()
);
assert!(
parse_octal_float(&mut parse("1.17_7777777_7777777_7777777"), false, &settings)
.is_err()
);
assert!(
parse_octal_float(&mut parse("0.17_7777777_7777777_7777770"), false, &settings)
.is_err()
);
assert!(
parse_octal_float(&mut parse("1.17_7777777_7777777_7777770"), false, &settings)
.is_err()
);
}
#[test]
fn reject_test_7() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = true;
assert!(
parse_octal_float(&mut parse("17_7777777_7777777_7777777"), false, &settings).is_err()
);
assert!(
parse_octal_float(&mut parse("17_7777777_7777777_7777770"), false, &settings).is_err()
);
}
#[test]
fn empty_parts_test() {
let mut settings = NumberParserSettings::new();
settings.permit_empty_whole = false;
settings.permit_empty_fraction = false;
assert!(parse_octal_float(&mut parse("10.e1"), false, &settings).is_err());
assert!(parse_octal_float(&mut parse("10."), false, &settings).is_err());
assert!(parse_octal_float(&mut parse(".10e1"), false, &settings).is_err());
assert!(parse_octal_float(&mut parse(".10"), false, &settings).is_err());
}
}