refactor variable value parsing to use predicates
This commit is contained in:
parent
7b4465250e
commit
6849cd578f
@ -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)?
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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."
|
||||||
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user