grass/src/parse/variable.rs

196 lines
7.1 KiB
Rust
Raw Normal View History

2020-06-16 22:34:01 -04:00
use codemap::Spanned;
use crate::{
common::Identifier,
error::SassResult,
utils::{
peek_ident_no_interpolation, read_until_closing_paren, read_until_closing_quote,
read_until_newline,
},
value::Value,
Token,
};
2020-07-05 10:13:49 -04:00
use super::Parser;
2020-06-16 22:34:01 -04:00
#[derive(Debug)]
struct VariableValue {
value: Spanned<Value>,
global: bool,
default: bool,
}
impl VariableValue {
pub const fn new(value: Spanned<Value>, global: bool, default: bool) -> Self {
Self {
value,
global,
default,
}
}
}
impl<'a> Parser<'a> {
pub(super) fn parse_variable_declaration(&mut self) -> SassResult<()> {
assert!(matches!(self.toks.next(), Some(Token { kind: '$', .. })));
let ident: Identifier = self.parse_identifier_no_interpolation(false)?.node.into();
self.whitespace();
if !matches!(self.toks.next(), Some(Token { kind: ':', .. })) {
return Err(("expected \":\".", self.span_before).into());
}
let value = self.parse_variable_value()?;
if value.global && !value.default {
self.global_scope
2020-07-03 12:38:20 -04:00
.insert_var(ident.clone(), value.value.clone());
2020-06-16 22:34:01 -04:00
}
if value.default {
2020-07-05 10:13:49 -04:00
if self.at_root && !self.flags.in_control_flow() {
2020-06-16 22:34:01 -04:00
if !self.global_scope.var_exists_no_global(&ident) {
2020-07-03 12:38:20 -04:00
self.global_scope.insert_var(ident, value.value);
2020-06-16 22:34:01 -04:00
}
} else {
if value.global && !self.global_scope.var_exists_no_global(&ident) {
self.global_scope
2020-07-03 12:38:20 -04:00
.insert_var(ident.clone(), value.value.clone());
2020-06-16 22:34:01 -04:00
}
if !self.scopes.last().var_exists_no_global(&ident) {
2020-07-03 12:38:20 -04:00
self.scopes.last_mut().insert_var(ident, value.value);
2020-06-16 22:34:01 -04:00
}
}
} else if self.at_root {
2020-07-05 10:13:49 -04:00
if self.flags.in_control_flow() {
2020-06-16 22:34:01 -04:00
if self.global_scope.var_exists_no_global(&ident) {
2020-07-03 12:38:20 -04:00
self.global_scope.insert_var(ident, value.value);
2020-06-16 22:34:01 -04:00
} else {
2020-07-03 12:38:20 -04:00
self.scopes.last_mut().insert_var(ident, value.value);
2020-06-16 22:34:01 -04:00
}
} else {
2020-07-03 12:38:20 -04:00
self.global_scope.insert_var(ident, value.value);
2020-06-16 22:34:01 -04:00
}
} else {
let len = self.scopes.len();
for (_, scope) in self
.scopes
.iter_mut()
.enumerate()
.filter(|(i, _)| *i != len)
{
if scope.var_exists_no_global(&ident) {
2020-07-03 12:38:20 -04:00
scope.insert_var(ident.clone(), value.value.clone());
2020-06-16 22:34:01 -04:00
}
}
2020-06-18 18:14:35 -04:00
if self.scopes.first().var_exists_no_global(&ident) {
self.scopes
.first_mut()
2020-07-03 12:38:20 -04:00
.insert_var(ident.clone(), value.value.clone());
2020-06-18 18:14:35 -04:00
}
2020-07-03 12:38:20 -04:00
self.scopes.last_mut().insert_var(ident, value.value);
2020-06-16 22:34:01 -04:00
}
Ok(())
}
fn parse_variable_value(&mut self) -> SassResult<VariableValue> {
let mut default = false;
let mut global = false;
let mut val_toks = Vec::new();
let mut nesting = 0;
while let Some(tok) = self.toks.peek() {
match tok.kind {
';' => {
self.toks.next();
break;
}
'\\' => {
val_toks.push(self.toks.next().unwrap());
if self.toks.peek().is_some() {
val_toks.push(self.toks.next().unwrap());
}
}
'"' | '\'' => {
let quote = self.toks.next().unwrap();
val_toks.push(quote);
val_toks.extend(read_until_closing_quote(self.toks, quote.kind)?);
}
'#' => {
val_toks.push(self.toks.next().unwrap());
match self.toks.peek() {
Some(Token { kind: '{', .. }) => nesting += 1,
Some(Token { kind: ';', .. }) => break,
Some(Token { kind: '}', .. }) => {
if nesting == 0 {
break;
} else {
nesting -= 1;
}
}
Some(..) | None => {}
}
if let Some(tok) = self.toks.next() {
val_toks.push(tok);
}
2020-06-16 22:34:01 -04:00
}
'{' => break,
'}' => {
if nesting == 0 {
break;
} else {
nesting -= 1;
val_toks.push(self.toks.next().unwrap());
}
}
'/' => {
let next = self.toks.next().unwrap();
match self.toks.peek() {
Some(Token { kind: '/', .. }) => read_until_newline(self.toks),
Some(..) | None => val_toks.push(next),
};
continue;
}
'(' => {
val_toks.push(self.toks.next().unwrap());
val_toks.extend(read_until_closing_paren(self.toks)?);
}
'!' => {
let pos = tok.pos();
2020-07-02 15:54:33 -04:00
match self.toks.peek_forward(1) {
Some(Token { kind: '=', .. }) => {
self.toks.reset_cursor();
val_toks.push(self.toks.next().unwrap());
continue;
}
Some(..) => {}
None => return Err(("Expected identifier.", pos).into()),
2020-06-16 22:34:01 -04:00
}
// todo: it should not be possible to declare the same flag more than once
let mut ident = peek_ident_no_interpolation(self.toks, false, pos)?;
ident.node.make_ascii_lowercase();
match ident.node.as_str() {
"global" => {
2020-07-02 15:54:33 -04:00
self.toks.truncate_iterator_to_cursor();
2020-06-16 22:34:01 -04:00
global = true;
}
"default" => {
2020-07-02 15:54:33 -04:00
self.toks.truncate_iterator_to_cursor();
2020-06-16 22:34:01 -04:00
default = true;
}
"important" => {
2020-06-19 22:47:06 -04:00
self.toks.reset_cursor();
2020-06-16 22:34:01 -04:00
val_toks.push(self.toks.next().unwrap());
continue;
}
_ => {
return Err(("Invalid flag name.", ident.span).into());
}
}
}
_ => val_toks.push(self.toks.next().unwrap()),
}
}
let val = self.parse_value_from_vec(val_toks)?;
Ok(VariableValue::new(val, global, default))
}
}