grass/src/utils/number.rs

132 lines
3.2 KiB
Rust
Raw Normal View History

use codemap::Spanned;
use peekmore::PeekMoreIterator;
2020-06-16 20:00:11 -04:00
use crate::{error::SassResult, Token};
#[derive(Debug)]
2020-04-28 12:15:10 -04:00
pub(crate) struct ParsedNumber {
2020-04-28 15:28:50 -04:00
/// The full number excluding the decimal
///
/// E.g. for `1.23`, this would be `"123"`
pub num: String,
2020-04-28 15:28:50 -04:00
/// The length of the decimal
///
/// E.g. for `1.23`, this would be `2`
pub dec_len: usize,
2020-04-28 15:28:50 -04:00
/// The number following e in a scientific notated number
///
/// E.g. for `1e23`, this would be `"23"`,
/// for `1`, this would be an empty string
2020-04-28 12:15:10 -04:00
// TODO: maybe we just return a bigint?
pub times_ten: String,
2020-04-28 15:28:50 -04:00
/// Whether or not `times_ten` is negative
///
/// E.g. for `1e-23` this would be `true`,
/// for `1e23` this would be `false`
2020-04-28 12:15:10 -04:00
pub times_ten_is_postive: bool,
}
impl ParsedNumber {
2020-04-28 15:14:44 -04:00
pub const fn new(
num: String,
dec_len: usize,
times_ten: String,
times_ten_is_postive: bool,
) -> Self {
2020-04-28 12:15:10 -04:00
Self {
num,
dec_len,
2020-04-28 12:15:10 -04:00
times_ten,
times_ten_is_postive,
}
}
}
2020-04-28 15:14:44 -04:00
pub(crate) fn eat_number<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
2020-04-28 12:15:10 -04:00
) -> SassResult<Spanned<ParsedNumber>> {
let mut whole = String::with_capacity(1);
2020-04-28 12:15:10 -04:00
// TODO: merge this span with chars
2020-06-18 03:09:24 -04:00
let span = toks.peek().unwrap().pos;
2020-04-28 12:15:10 -04:00
eat_whole_number(toks, &mut whole);
if toks.peek().is_none() {
2020-04-28 12:15:10 -04:00
return Ok(Spanned {
2020-04-28 15:28:50 -04:00
node: ParsedNumber::new(whole, 0, String::new(), true),
2020-04-28 12:15:10 -04:00
span,
});
}
let mut dec = String::new();
2020-04-21 18:22:26 -04:00
let next_tok = *toks.peek().unwrap();
if next_tok.kind == '.' {
toks.next();
2020-04-28 12:15:10 -04:00
eat_whole_number(toks, &mut dec);
2020-04-28 15:49:19 -04:00
if dec.is_empty() {
return Err(("Expected digit.", next_tok.pos()).into());
}
}
2020-04-28 12:15:10 -04:00
let mut times_ten = String::new();
let mut times_ten_is_postive = true;
2020-05-24 17:41:24 -04:00
#[allow(clippy::never_loop)]
2020-04-28 12:15:10 -04:00
loop {
if let Some(Token { kind: 'e', .. }) | Some(Token { kind: 'E', .. }) = toks.peek() {
let t = if let Some(tok) = toks.peek_forward(1) {
*tok
} else {
break;
};
match t.kind {
'-' => {
2020-04-28 12:15:10 -04:00
toks.next();
times_ten_is_postive = false;
}
'0'..='9' => {}
_ => break,
}
2020-04-28 12:15:10 -04:00
toks.next();
2020-04-28 12:15:10 -04:00
eat_whole_number(toks, &mut times_ten);
if times_ten.is_empty() && !times_ten_is_postive {
return Err(("Expected digit.", toks.peek().unwrap_or(&t).pos).into());
} else if times_ten.len() > 2 {
return Err(("Exponent too large.", toks.peek().unwrap_or(&t).pos).into());
2020-04-28 12:15:10 -04:00
}
}
break;
}
2020-06-19 22:47:06 -04:00
toks.reset_cursor();
2020-04-28 12:15:10 -04:00
whole.push_str(&dec);
2020-04-28 12:15:10 -04:00
Ok(Spanned {
2020-04-28 15:28:50 -04:00
node: ParsedNumber::new(whole, dec.len(), times_ten, times_ten_is_postive),
2020-04-28 12:15:10 -04:00
span,
})
}
2020-07-04 20:50:53 -04:00
pub(crate) fn eat_whole_number<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
buf: &mut String,
) {
2020-04-28 12:15:10 -04:00
while let Some(c) = toks.peek() {
if !c.kind.is_ascii_digit() {
2020-04-28 12:15:10 -04:00
break;
}
let tok = toks.next().unwrap();
buf.push(tok.kind);
}
}