diff --git a/src/utils/number.rs b/src/utils/number.rs index 5e0e747..a3164f3 100644 --- a/src/utils/number.rs +++ b/src/utils/number.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::iter::Iterator; use codemap::Spanned; @@ -9,18 +8,28 @@ use crate::error::SassResult; use crate::Token; pub(crate) struct ParsedNumber { - pub v: String, + pub num: String, + pub dec_len: usize, // TODO: maybe we just return a bigint? - pub times_ten: Cow<'static, str>, + pub times_ten: String, pub times_ten_is_postive: bool, + pub is_float: bool, } impl ParsedNumber { - pub fn new(v: String, times_ten: Cow<'static, str>, times_ten_is_postive: bool) -> Self { + pub fn new( + num: String, + dec_len: usize, + times_ten: String, + times_ten_is_postive: bool, + is_float: bool, + ) -> Self { Self { - v, + num, + dec_len, times_ten, times_ten_is_postive, + is_float, } } } @@ -28,7 +37,7 @@ impl ParsedNumber { pub(crate) fn eat_number<'a, I: Iterator>( toks: &mut PeekMoreIterator, ) -> SassResult> { - let mut whole = String::new(); + let mut whole = String::with_capacity(1); // TODO: merge this span with chars let span = if let Some(tok) = toks.peek() { tok.pos() @@ -39,22 +48,23 @@ pub(crate) fn eat_number<'a, I: Iterator>( if toks.peek().is_none() { return Ok(Spanned { - node: ParsedNumber::new(whole, Cow::from("0"), true), + node: ParsedNumber::new(whole, 0, String::new(), true, false), span, }); } let mut dec = String::new(); + let mut is_float = false; let next_tok = *toks.peek().unwrap(); if next_tok.kind == '.' { toks.next(); - dec.push('.'); + is_float = true; eat_whole_number(toks, &mut dec); } - if dec.len() == 1 { + if dec.is_empty() && is_float { return Err(("Expected digit.", next_tok.pos()).into()); } @@ -101,12 +111,14 @@ pub(crate) fn eat_number<'a, I: Iterator>( Ok(Spanned { node: ParsedNumber::new( whole, + dec.len(), if !times_ten.is_empty() { - Cow::from(times_ten) + times_ten } else { - Cow::from("0") + String::new() }, times_ten_is_postive, + is_float, ), span, }) diff --git a/src/value/parse.rs b/src/value/parse.rs index 80f8649..68b7ef5 100644 --- a/src/value/parse.rs +++ b/src/value/parse.rs @@ -3,7 +3,7 @@ use std::mem; use num_bigint::BigInt; use num_rational::BigRational; -use num_traits::{pow, One, ToPrimitive}; +use num_traits::{pow, One, ToPrimitive, Zero}; use codemap::{Span, Spanned}; @@ -630,36 +630,25 @@ impl Value { Unit::None }; - let times_ten = pow( - BigInt::from(10), - val.times_ten - .parse::() - .unwrap() - .to_usize() - .ok_or(("Exponent too large (expected usize).", span))?, - ); - - let n = if let Ok(v) = val.v.parse::() { - // the number is an integer! - v - // the number is floating point + let times_ten = if val.times_ten.is_empty() { + BigInt::zero() } else { - let mut num = String::new(); - let mut chars = val.v.chars(); - let mut num_dec = 0; - while let Some(c) = chars.next() { - if c == '.' { - break; - } - num.push(c); - } - for c in chars { - num_dec += 1; - num.push(c); - } - BigRational::new(num.parse().unwrap(), pow(BigInt::from(10), num_dec)) + pow( + BigInt::from(10), + val.times_ten + .parse::() + .unwrap() + .to_usize() + .ok_or(("Exponent too large (expected usize).", span))?, + ) + }; + + let n = if val.is_float { + BigRational::new(val.num.parse().unwrap(), pow(BigInt::from(10), val.dec_len)) + } else { + BigRational::new_raw(val.num.parse::().unwrap(), BigInt::one()) } * if val.times_ten_is_postive { - BigRational::new(times_ten, BigInt::one()) + BigRational::new_raw(times_ten, BigInt::one()) } else { BigRational::new(BigInt::one(), times_ten) };