grass/src/utils/variables.rs
2020-04-23 21:30:25 -04:00

131 lines
4.0 KiB
Rust

use std::iter::Iterator;
use codemap::Spanned;
use peekmore::PeekMoreIterator;
use crate::error::SassResult;
use crate::selector::Selector;
use crate::value::Value;
use crate::{Scope, Token};
use super::{
devour_whitespace, peek_ident_no_interpolation, read_until_closing_paren,
read_until_closing_quote, read_until_newline,
};
pub(crate) struct VariableDecl {
pub val: Spanned<Value>,
pub default: bool,
pub global: bool,
}
impl VariableDecl {
pub const fn new(val: Spanned<Value>, default: bool, global: bool) -> VariableDecl {
VariableDecl {
val,
default,
global,
}
}
}
pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
scope: &Scope,
super_selector: &Selector,
) -> SassResult<VariableDecl> {
devour_whitespace(toks);
let mut default = false;
let mut global = false;
let mut val_toks = Vec::new();
let mut nesting = 0;
while let Some(tok) = toks.peek() {
match tok.kind {
';' => {
toks.next();
break;
}
'\\' => {
val_toks.push(toks.next().unwrap());
if toks.peek().is_some() {
val_toks.push(toks.next().unwrap());
}
}
'"' | '\'' => {
let quote = toks.next().unwrap();
val_toks.push(quote);
val_toks.extend(read_until_closing_quote(toks, quote.kind));
}
'#' => {
val_toks.push(toks.next().unwrap());
match toks.peek().unwrap().kind {
'{' => nesting += 1,
';' => break,
'}' => {
if nesting == 0 {
break;
} else {
nesting -= 1;
}
}
_ => {}
}
val_toks.push(toks.next().unwrap());
}
'{' => break,
'}' => {
if nesting == 0 {
break;
} else {
nesting -= 1;
val_toks.push(toks.next().unwrap());
}
}
'/' => {
let next = toks.next().unwrap();
match toks.peek().unwrap().kind {
'/' => read_until_newline(toks),
_ => val_toks.push(next),
};
continue;
}
'(' => {
val_toks.push(toks.next().unwrap());
val_toks.extend(read_until_closing_paren(toks));
}
'!' => {
let pos = tok.pos();
if toks.peek_forward(1).is_none() {
return Err(("Expected identifier.", pos).into());
}
// todo: it should not be possible to declare the same flag more than once
let ident = peek_ident_no_interpolation(toks, false)?;
match ident.node.to_ascii_lowercase().as_str() {
"global" => {
toks.take(7).for_each(drop);
global = true;
}
"default" => {
toks.take(8).for_each(drop);
default = true;
}
"important" => {
toks.reset_view();
val_toks.push(toks.next().unwrap());
continue;
}
_ => {
return Err(("Invalid flag name.", ident.span).into());
}
}
}
_ => val_toks.push(toks.next().unwrap()),
}
}
devour_whitespace(toks);
let val = Value::from_vec(val_toks, scope, super_selector)?;
Ok(VariableDecl::new(val, default, global))
}