refactor variable value parsing to use predicates

This commit is contained in:
Connor Skees 2020-08-19 07:13:32 -04:00
parent 7b4465250e
commit 6849cd578f
3 changed files with 69 additions and 107 deletions

View File

@ -265,7 +265,7 @@ impl<'a> Parser<'a> {
self.expect_char(':')?; self.expect_char(':')?;
let VariableValue { let VariableValue {
val_toks, var_value,
global, global,
default, default,
} = self.parse_variable_value()?; } = self.parse_variable_value()?;
@ -282,7 +282,7 @@ impl<'a> Parser<'a> {
return Ok(()); return Ok(());
} }
let value = self.parse_value_from_vec(val_toks, true)?; let value = var_value?;
self.modules self.modules
.get_mut(module, variable.span)? .get_mut(module, variable.span)?

View File

@ -1,23 +1,20 @@
use crate::{ use codemap::Spanned;
common::Identifier,
error::SassResult, use crate::{common::Identifier, error::SassResult, value::Value, Token};
utils::{peek_ident_no_interpolation, read_until_closing_paren, read_until_closing_quote},
Token,
};
use super::Parser; use super::Parser;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct VariableValue { pub(crate) struct VariableValue {
pub val_toks: Vec<Token>, pub var_value: SassResult<Spanned<Value>>,
pub global: bool, pub global: bool,
pub default: bool, pub default: bool,
} }
impl VariableValue { impl VariableValue {
pub const fn new(val_toks: Vec<Token>, global: bool, default: bool) -> Self { pub const fn new(var_value: SassResult<Spanned<Value>>, global: bool, default: bool) -> Self {
Self { Self {
val_toks, var_value,
global, global,
default, default,
} }
@ -33,7 +30,7 @@ impl<'a> Parser<'a> {
self.expect_char(':')?; self.expect_char(':')?;
let VariableValue { let VariableValue {
val_toks, var_value,
global, global,
default, default,
} = self.parse_variable_value()?; } = self.parse_variable_value()?;
@ -47,22 +44,22 @@ impl<'a> Parser<'a> {
} else if let Some(value) = config_val { } else if let Some(value) = config_val {
value value
} else { } else {
self.parse_value_from_vec(val_toks, true)?.node var_value?.node
} }
} else if self.at_root && self.flags.in_control_flow() { } else if self.at_root && self.flags.in_control_flow() {
if self.global_scope.default_var_exists(ident) { if self.global_scope.default_var_exists(ident) {
return Ok(()); return Ok(());
} }
self.parse_value_from_vec(val_toks, true)?.node var_value?.node
} else if self.at_root { } else if self.at_root {
self.parse_value_from_vec(val_toks, true)?.node var_value?.node
} else { } else {
if self.scopes.default_var_exists(ident) { if self.scopes.default_var_exists(ident) {
return Ok(()); return Ok(());
} }
self.parse_value_from_vec(val_toks, true)?.node var_value?.node
}; };
if self.at_root && self.global_scope.var_exists(ident) { if self.at_root && self.global_scope.var_exists(ident) {
@ -89,7 +86,7 @@ impl<'a> Parser<'a> {
return Ok(()); return Ok(());
} }
let value = self.parse_value_from_vec(val_toks, true)?.node; let value = var_value?.node;
if global { if global {
self.global_scope.insert_var(ident, value.clone()); self.global_scope.insert_var(ident, value.clone());
@ -115,101 +112,49 @@ impl<'a> Parser<'a> {
let mut default = false; let mut default = false;
let mut global = false; let mut global = false;
let mut val_toks = Vec::new(); let value = self.parse_value(true, &|toks| {
let mut nesting = 0; if matches!(toks.peek(), Some(Token { kind: '!', .. })) {
while let Some(tok) = self.toks.peek() { let is_important = matches!(toks.peek_next(), Some(Token { kind: 'i', .. }) | Some(Token { kind: 'I', .. }) | Some(Token { kind: '=', .. }));
match tok.kind { toks.reset_cursor();
';' => { !is_important
self.toks.next(); } else {
break; false
}
});
// todo: it should not be possible to declare the same flag more than once
while self.consume_char_if_exists('!') {
let flag = self.parse_identifier_no_interpolation(false)?;
match flag.node.as_str() {
"global" => {
self.toks.truncate_iterator_to_cursor();
global = true;
} }
'\\' => { "default" => {
val_toks.push(self.toks.next().unwrap()); self.toks.truncate_iterator_to_cursor();
if self.toks.peek().is_some() { default = true;
val_toks.push(self.toks.next().unwrap());
}
} }
'"' | '\'' => { _ => {
let quote = self.toks.next().unwrap(); return Err(("Invalid flag name.", flag.span).into());
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() { self.whitespace_or_comment();
Some(Token { kind: '{', .. }) => nesting += 1, }
Some(Token { kind: ';', .. }) => break,
Some(Token { kind: '}', .. }) => { match self.toks.peek() {
if nesting == 0 { Some(Token { kind: ';', .. }) => {
break; self.toks.next();
} else { }
nesting -= 1; Some(Token { kind: '}', .. }) => {}
} Some(..) | None => {
} value?;
Some(..) | None => {} self.expect_char(';')?;
} unreachable!()
if let Some(tok) = self.toks.next() {
val_toks.push(tok);
}
}
'{' => 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: '/', .. }) => self.read_until_newline(),
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();
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()),
}
// 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" => {
self.toks.truncate_iterator_to_cursor();
global = true;
}
"default" => {
self.toks.truncate_iterator_to_cursor();
default = true;
}
"important" => {
self.toks.reset_cursor();
val_toks.push(self.toks.next().unwrap());
continue;
}
_ => {
return Err(("Invalid flag name.", ident.span).into());
}
}
}
_ => val_toks.push(self.toks.next().unwrap()),
} }
} }
Ok(VariableValue::new(val_toks, global, default)) Ok(VariableValue::new(value, global, default))
} }
} }

View File

@ -376,6 +376,19 @@ test!(
); );
error!(ends_with_bang, "$a: red !;", "Error: Expected identifier."); error!(ends_with_bang, "$a: red !;", "Error: Expected identifier.");
error!(unknown_flag, "$a: red !foo;", "Error: Invalid flag name."); error!(unknown_flag, "$a: red !foo;", "Error: Invalid flag name.");
error!(
flag_in_middle_of_value,
"$a: a !default b;", "Error: expected \";\"."
);
// note: dart-sass expects !important
error!(
no_value_only_flag,
"$a: !default;", "Error: Expected expression."
);
error!(
uppercase_flag,
"$a: 1 !GLOBAL;", "Error: Invalid flag name."
);
error!( error!(
undefined_variable, undefined_variable,
"a {color: $a;}", "Error: Undefined variable." "a {color: $a;}", "Error: Undefined variable."
@ -388,3 +401,7 @@ error!(
nothing_after_hash_in_variable_decl, nothing_after_hash_in_variable_decl,
"$color: #", "Error: Expected identifier." "$color: #", "Error: Expected identifier."
); );
error!(
only_semicolon_after_hash_in_variable_decl,
"$color: #;", "Error: Expected identifier."
);