optimize parsing of numbers

This makes parsing of floats roughly 10x faster
This commit is contained in:
ConnorSkees 2020-04-28 13:18:54 -04:00
parent 2ee4396978
commit a183a9ffa2
2 changed files with 41 additions and 40 deletions

View File

@ -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<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
) -> SassResult<Spanned<ParsedNumber>> {
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<Item = Token>>(
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<Item = Token>>(
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,
})

View File

@ -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::<BigInt>()
.unwrap()
.to_usize()
.ok_or(("Exponent too large (expected usize).", span))?,
);
let n = if let Ok(v) = val.v.parse::<BigRational>() {
// 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::<BigInt>()
.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::<BigInt>().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)
};