Fix negative number literals

This commit is contained in:
Richard Feldman 2019-09-04 01:23:50 -04:00
parent 6cfc98b415
commit 3db0e9e5c6

View file

@ -11,10 +11,8 @@ pub fn number_literal<'a>() -> impl Parser<'a, Expr<'a>> {
match chars.next() { match chars.next() {
Some(first_ch) => { Some(first_ch) => {
if first_ch == '-' { if first_ch == '-' || first_ch.is_ascii_digit() {
parse_number_literal(Sign::Negative, first_ch, &mut chars, arena, state) parse_number_literal(first_ch, &mut chars, arena, state)
} else if first_ch.is_ascii_digit() {
parse_number_literal(Sign::Positive, first_ch, &mut chars, arena, state)
} else { } else {
Err(unexpected( Err(unexpected(
first_ch, first_ch,
@ -31,7 +29,6 @@ pub fn number_literal<'a>() -> impl Parser<'a, Expr<'a>> {
#[inline(always)] #[inline(always)]
fn parse_number_literal<'a, I>( fn parse_number_literal<'a, I>(
sign: Sign,
first_ch: char, first_ch: char,
chars: &mut I, chars: &mut I,
arena: &'a Bump, arena: &'a Bump,
@ -40,22 +37,20 @@ fn parse_number_literal<'a, I>(
where where
I: Iterator<Item = char>, I: Iterator<Item = char>,
{ {
let mut digits_before_decimal = String::with_capacity_in(1, arena); let mut before_decimal = String::with_capacity_in(1, arena);
let mut digits_after_decimal = String::new_in(arena); let mut after_decimal = String::new_in(arena);
let mut chars_skipped = 0;
let mut has_decimal_point = false; let mut has_decimal_point = false;
let mut chars_skipped = 0;
if sign == Sign::Positive { before_decimal.push(first_ch);
digits_before_decimal.push(first_ch);
}
while let Some(next_ch) = chars.next() { while let Some(next_ch) = chars.next() {
match next_ch { match next_ch {
digit if next_ch.is_ascii_digit() => { digit if next_ch.is_ascii_digit() => {
if has_decimal_point { if has_decimal_point {
digits_after_decimal.push(digit); after_decimal.push(digit);
} else { } else {
digits_before_decimal.push(digit); before_decimal.push(digit);
} }
} }
'_' => { '_' => {
@ -65,8 +60,7 @@ where
'.' => { '.' => {
if has_decimal_point { if has_decimal_point {
// You only get one decimal point! // You only get one decimal point!
let len = let len = before_decimal.len() + after_decimal.len() + chars_skipped;
digits_before_decimal.len() + digits_after_decimal.len() + chars_skipped;
return Err(unexpected('.', len, state, Attempting::NumberLiteral)); return Err(unexpected('.', len, state, Attempting::NumberLiteral));
} else { } else {
@ -75,10 +69,9 @@ where
} }
} }
invalid_char => { invalid_char => {
if digits_before_decimal.is_empty() { if before_decimal.is_empty() {
// No digits! We likely parsed a minus sign that's actually an operator. // No digits! We likely parsed a minus sign that's actually an operator.
let len = let len = before_decimal.len() + after_decimal.len() + chars_skipped;
digits_before_decimal.len() + digits_after_decimal.len() + chars_skipped;
return Err(unexpected( return Err(unexpected(
invalid_char, invalid_char,
len, len,
@ -96,35 +89,32 @@ where
// At this point we have a number, and will definitely succeed. // At this point we have a number, and will definitely succeed.
// If the number is malformed (too large to fit), we'll succeed with // If the number is malformed (too large to fit), we'll succeed with
// an appropriate Expr which records that. // an appropriate Expr which records that.
let total_chars_parsed = digits_before_decimal.len() + chars_skipped; let expr = if has_decimal_point {
let state = state.advance_without_indenting(total_chars_parsed);
match digits_before_decimal.parse::<i64>() {
Ok(int_val) => {
if has_decimal_point {
let mut f64_buf = String::with_capacity_in( let mut f64_buf = String::with_capacity_in(
digits_before_decimal.len() + 1 + digits_after_decimal.len(), before_decimal.len()
// +1 for the decimal point itself
+ 1
+ after_decimal.len(),
arena, arena,
); );
f64_buf.push_str(&digits_before_decimal); f64_buf.push_str(&before_decimal);
f64_buf.push('.'); f64_buf.push('.');
f64_buf.push_str(&digits_after_decimal); f64_buf.push_str(&after_decimal);
match f64_buf.parse::<f64>() { match f64_buf.parse::<f64>() {
Ok(float) => Ok((Expr::Float(float), state)), Ok(float) => Expr::Float(float),
Err(_) => Ok((Expr::MalformedNumber(Problem::TooLarge), state)), Err(_) => Expr::MalformedNumber(Problem::TooLarge),
} }
} else { } else {
Ok((Expr::Int(int_val), state)) match before_decimal.parse::<i64>() {
Ok(int_val) => Expr::Int(int_val),
Err(_) => Expr::MalformedNumber(Problem::TooLarge),
} }
} };
Err(_) => Ok((Expr::MalformedNumber(Problem::TooLarge), state)),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)] let total_chars_parsed = before_decimal.len() + chars_skipped;
enum Sign { let state = state.advance_without_indenting(total_chars_parsed);
Positive,
Negative, Ok((expr, state))
} }