Initial implementation of !global (some issues remain)
This commit is contained in:
parent
d560f13289
commit
061694bd63
@ -1,8 +1,9 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use crate::common::{Scope, Symbol};
|
use crate::common::Symbol;
|
||||||
use crate::error::SassResult;
|
use crate::error::SassResult;
|
||||||
|
use crate::scope::Scope;
|
||||||
use crate::selector::Selector;
|
use crate::selector::Selector;
|
||||||
use crate::utils::{devour_whitespace, devour_whitespace_or_comment};
|
use crate::utils::{devour_whitespace, devour_whitespace_or_comment};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
@ -3,10 +3,11 @@ use std::iter::Peekable;
|
|||||||
|
|
||||||
use num_traits::cast::ToPrimitive;
|
use num_traits::cast::ToPrimitive;
|
||||||
|
|
||||||
use crate::common::{Keyword, Pos, Scope, Symbol};
|
use crate::common::{Keyword, Pos, Symbol};
|
||||||
use crate::error::SassResult;
|
use crate::error::SassResult;
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::mixin::Mixin;
|
use crate::mixin::Mixin;
|
||||||
|
use crate::scope::Scope;
|
||||||
use crate::selector::Selector;
|
use crate::selector::Selector;
|
||||||
use crate::units::Unit;
|
use crate::units::Unit;
|
||||||
use crate::utils::{devour_whitespace, devour_whitespace_or_comment};
|
use crate::utils::{devour_whitespace, devour_whitespace_or_comment};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use crate::common::Scope;
|
|
||||||
use crate::error::SassResult;
|
use crate::error::SassResult;
|
||||||
|
use crate::scope::Scope;
|
||||||
use crate::selector::Selector;
|
use crate::selector::Selector;
|
||||||
use crate::{eat_expr, Expr, RuleSet, Stmt, Token};
|
use crate::{eat_expr, Expr, RuleSet, Stmt, Token};
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use super::parse::eat_stmts;
|
use super::parse::eat_stmts;
|
||||||
use crate::common::{Scope, Symbol};
|
use crate::common::Symbol;
|
||||||
use crate::error::SassResult;
|
use crate::error::SassResult;
|
||||||
|
use crate::scope::Scope;
|
||||||
use crate::selector::Selector;
|
use crate::selector::Selector;
|
||||||
use crate::utils::{devour_whitespace, parse_interpolation};
|
use crate::utils::{devour_whitespace, parse_interpolation};
|
||||||
use crate::{RuleSet, Stmt, Token, TokenKind};
|
use crate::{RuleSet, Stmt, Token, TokenKind};
|
||||||
|
@ -2,8 +2,8 @@ use once_cell::sync::Lazy;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::args::CallArgs;
|
use crate::args::CallArgs;
|
||||||
use crate::common::Scope;
|
|
||||||
use crate::error::SassResult;
|
use crate::error::SassResult;
|
||||||
|
use crate::scope::Scope;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
|
|
||||||
use crate::error::SassResult;
|
|
||||||
use crate::function::Function;
|
|
||||||
use crate::mixin::Mixin;
|
|
||||||
use crate::value::Value;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Symbol {
|
pub enum Symbol {
|
||||||
/// .
|
/// .
|
||||||
@ -220,6 +214,7 @@ pub enum Keyword {
|
|||||||
False,
|
False,
|
||||||
Null,
|
Null,
|
||||||
Default,
|
Default,
|
||||||
|
Global,
|
||||||
From(String),
|
From(String),
|
||||||
To(String),
|
To(String),
|
||||||
Through(String),
|
Through(String),
|
||||||
@ -243,7 +238,7 @@ impl Display for Keyword {
|
|||||||
Self::False => write!(f, "false"),
|
Self::False => write!(f, "false"),
|
||||||
Self::Null => write!(f, "null"),
|
Self::Null => write!(f, "null"),
|
||||||
Self::Default => write!(f, "!default"),
|
Self::Default => write!(f, "!default"),
|
||||||
// todo!(maintain casing for keywords)
|
Self::Global => write!(f, "!global"),
|
||||||
Self::From(s) => write!(f, "{}", s),
|
Self::From(s) => write!(f, "{}", s),
|
||||||
Self::To(s) => write!(f, "{}", s),
|
Self::To(s) => write!(f, "{}", s),
|
||||||
Self::Through(s) => write!(f, "{}", s),
|
Self::Through(s) => write!(f, "{}", s),
|
||||||
@ -269,6 +264,7 @@ impl Into<&'static str> for Keyword {
|
|||||||
Self::False => "false",
|
Self::False => "false",
|
||||||
Self::Null => "null",
|
Self::Null => "null",
|
||||||
Self::Default => "!default",
|
Self::Default => "!default",
|
||||||
|
Self::Global => "!global",
|
||||||
Self::From(_) => "from",
|
Self::From(_) => "from",
|
||||||
Self::To(_) => "to",
|
Self::To(_) => "to",
|
||||||
Self::Through(_) => "through",
|
Self::Through(_) => "through",
|
||||||
@ -296,6 +292,7 @@ impl TryFrom<&str> for Keyword {
|
|||||||
"false" => Ok(Self::False),
|
"false" => Ok(Self::False),
|
||||||
"null" => Ok(Self::Null),
|
"null" => Ok(Self::Null),
|
||||||
"default" => Ok(Self::Default),
|
"default" => Ok(Self::Default),
|
||||||
|
"global" => Ok(Self::Global),
|
||||||
"from" => Ok(Self::From(kw.to_owned())),
|
"from" => Ok(Self::From(kw.to_owned())),
|
||||||
"to" => Ok(Self::To(kw.to_owned())),
|
"to" => Ok(Self::To(kw.to_owned())),
|
||||||
"through" => Ok(Self::Through(kw.to_owned())),
|
"through" => Ok(Self::Through(kw.to_owned())),
|
||||||
@ -353,75 +350,6 @@ impl Display for Pos {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct Scope {
|
|
||||||
vars: HashMap<String, Value>,
|
|
||||||
mixins: HashMap<String, Mixin>,
|
|
||||||
functions: HashMap<String, Function>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Scope {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
vars: HashMap::new(),
|
|
||||||
mixins: HashMap::new(),
|
|
||||||
functions: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_var(&self, v: &str) -> SassResult<&Value> {
|
|
||||||
match self.vars.get(&v.replace('_', "-")) {
|
|
||||||
Some(v) => Ok(v),
|
|
||||||
None => Err("Undefined variable.".into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_var(&mut self, s: &str, v: Value) -> SassResult<Option<Value>> {
|
|
||||||
Ok(self.vars.insert(s.replace('_', "-"), v.eval()?))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn var_exists(&self, v: &str) -> bool {
|
|
||||||
self.vars.contains_key(&v.replace('_', "-"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mixin(&self, v: &str) -> SassResult<&Mixin> {
|
|
||||||
match self.mixins.get(&v.replace('_', "-")) {
|
|
||||||
Some(v) => Ok(v),
|
|
||||||
None => Err("Undefined mixin.".into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_mixin(&mut self, s: &str, v: Mixin) -> Option<Mixin> {
|
|
||||||
self.mixins.insert(s.replace('_', "-"), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mixin_exists(&self, v: &str) -> bool {
|
|
||||||
self.mixins.contains_key(&v.replace('_', "-"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_fn(&self, v: &str) -> SassResult<&Function> {
|
|
||||||
match self.functions.get(&v.replace('_', "-")) {
|
|
||||||
Some(v) => Ok(v),
|
|
||||||
None => Err("Undefined function.".into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_fn(&mut self, s: &str, v: Function) -> Option<Function> {
|
|
||||||
self.functions.insert(s.replace('_', "-"), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fn_exists(&self, v: &str) -> bool {
|
|
||||||
self.functions.contains_key(&v.replace('_', "-"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn extend(&mut self, other: Scope) {
|
|
||||||
self.vars.extend(other.vars);
|
|
||||||
self.mixins.extend(other.mixins);
|
|
||||||
self.functions.extend(other.functions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub(crate) enum QuoteKind {
|
pub(crate) enum QuoteKind {
|
||||||
Single,
|
Single,
|
||||||
|
@ -2,8 +2,9 @@ use std::iter::Peekable;
|
|||||||
|
|
||||||
use crate::args::{eat_func_args, CallArgs, FuncArgs};
|
use crate::args::{eat_func_args, CallArgs, FuncArgs};
|
||||||
use crate::atrule::AtRule;
|
use crate::atrule::AtRule;
|
||||||
use crate::common::{Scope, Symbol};
|
use crate::common::Symbol;
|
||||||
use crate::error::SassResult;
|
use crate::error::SassResult;
|
||||||
|
use crate::scope::Scope;
|
||||||
use crate::selector::Selector;
|
use crate::selector::Selector;
|
||||||
use crate::utils::devour_whitespace;
|
use crate::utils::devour_whitespace;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::common::Scope;
|
|
||||||
use crate::error::SassResult;
|
use crate::error::SassResult;
|
||||||
|
use crate::scope::Scope;
|
||||||
use crate::{Stmt, StyleSheet};
|
use crate::{Stmt, StyleSheet};
|
||||||
|
|
||||||
pub(crate) fn import<P: AsRef<Path>>(path: P) -> SassResult<(Vec<Stmt>, Scope)> {
|
pub(crate) fn import<P: AsRef<Path>>(path: P) -> SassResult<(Vec<Stmt>, Scope)> {
|
||||||
|
@ -180,6 +180,11 @@ impl<'a> Lexer<'a> {
|
|||||||
assert_char!(self, 'e' 'f' 'a' 'u' 'l' 't');
|
assert_char!(self, 'e' 'f' 'a' 'u' 'l' 't');
|
||||||
TokenKind::Keyword(Keyword::Default)
|
TokenKind::Keyword(Keyword::Default)
|
||||||
}
|
}
|
||||||
|
Some('g') | Some('G') => {
|
||||||
|
self.buf.next();
|
||||||
|
assert_char!(self, 'l' 'o' 'b' 'a' 'l');
|
||||||
|
TokenKind::Keyword(Keyword::Global)
|
||||||
|
}
|
||||||
Some('=') => {
|
Some('=') => {
|
||||||
self.buf.next();
|
self.buf.next();
|
||||||
TokenKind::Op(Op::NotEqual)
|
TokenKind::Op(Op::NotEqual)
|
||||||
|
57
src/lib.rs
57
src/lib.rs
@ -85,7 +85,7 @@ use std::iter::{Iterator, Peekable};
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::atrule::{AtRule, AtRuleKind};
|
use crate::atrule::{AtRule, AtRuleKind};
|
||||||
use crate::common::{Keyword, Op, Pos, Scope, Symbol, Whitespace};
|
use crate::common::{Keyword, Op, Pos, Symbol, Whitespace};
|
||||||
use crate::css::Css;
|
use crate::css::Css;
|
||||||
use crate::error::SassError;
|
use crate::error::SassError;
|
||||||
pub use crate::error::SassResult;
|
pub use crate::error::SassResult;
|
||||||
@ -94,6 +94,7 @@ use crate::function::Function;
|
|||||||
use crate::imports::import;
|
use crate::imports::import;
|
||||||
use crate::lexer::Lexer;
|
use crate::lexer::Lexer;
|
||||||
use crate::mixin::{eat_include, Mixin};
|
use crate::mixin::{eat_include, Mixin};
|
||||||
|
use crate::scope::{insert_global_var, Scope, GLOBAL_SCOPE};
|
||||||
use crate::selector::Selector;
|
use crate::selector::Selector;
|
||||||
use crate::style::Style;
|
use crate::style::Style;
|
||||||
use crate::utils::{devour_whitespace, eat_variable_value, IsComment, IsWhitespace, VariableDecl};
|
use crate::utils::{devour_whitespace, eat_variable_value, IsComment, IsWhitespace, VariableDecl};
|
||||||
@ -111,12 +112,18 @@ mod function;
|
|||||||
mod imports;
|
mod imports;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
mod mixin;
|
mod mixin;
|
||||||
|
mod scope;
|
||||||
mod selector;
|
mod selector;
|
||||||
mod style;
|
mod style;
|
||||||
mod units;
|
mod units;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
|
pub(crate) fn error<E: Into<String>>(msg: E) -> ! {
|
||||||
|
eprintln!("Error: {}", msg.into());
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub(crate) struct Token {
|
pub(crate) struct Token {
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
@ -391,7 +398,7 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
| TokenKind::Symbol(Symbol::Mul)
|
| TokenKind::Symbol(Symbol::Mul)
|
||||||
| TokenKind::Symbol(Symbol::Percent)
|
| TokenKind::Symbol(Symbol::Percent)
|
||||||
| TokenKind::Symbol(Symbol::Period) => rules
|
| TokenKind::Symbol(Symbol::Period) => rules
|
||||||
.extend(self.eat_rules(&Selector::new(), &mut self.global_scope.clone())?),
|
.extend(self.eat_rules(&Selector::new(), &mut GLOBAL_SCOPE.with(|s| s.borrow().clone()))?),
|
||||||
TokenKind::Whitespace(_) => {
|
TokenKind::Whitespace(_) => {
|
||||||
self.lexer.next();
|
self.lexer.next();
|
||||||
continue;
|
continue;
|
||||||
@ -415,11 +422,16 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
{
|
{
|
||||||
self.error(pos, "unexpected variable use at toplevel");
|
self.error(pos, "unexpected variable use at toplevel");
|
||||||
}
|
}
|
||||||
let VariableDecl { val, default } =
|
let VariableDecl { val, default, .. } =
|
||||||
eat_variable_value(&mut self.lexer, &self.global_scope, &Selector::new())?;
|
eat_variable_value(&mut self.lexer, &GLOBAL_SCOPE.with(|s| s.borrow().clone()), &Selector::new())?;
|
||||||
if !default || self.global_scope.get_var(&name).is_err() {
|
GLOBAL_SCOPE.with(|s| {
|
||||||
self.global_scope.insert_var(&name, val)?;
|
if !default || s.borrow().get_var(&name).is_err() {
|
||||||
}
|
match s.borrow_mut().insert_var(&name, val) {
|
||||||
|
Ok(..) => {},
|
||||||
|
Err(e) => error(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
TokenKind::MultilineComment(_) => {
|
TokenKind::MultilineComment(_) => {
|
||||||
let comment = match self
|
let comment = match self
|
||||||
@ -435,7 +447,7 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
}
|
}
|
||||||
TokenKind::AtRule(AtRuleKind::Include) => rules.extend(eat_include(
|
TokenKind::AtRule(AtRuleKind::Include) => rules.extend(eat_include(
|
||||||
&mut self.lexer,
|
&mut self.lexer,
|
||||||
&self.global_scope,
|
&GLOBAL_SCOPE.with(|s| s.borrow().clone()),
|
||||||
&Selector::new(),
|
&Selector::new(),
|
||||||
)?),
|
)?),
|
||||||
TokenKind::AtRule(AtRuleKind::Import) => {
|
TokenKind::AtRule(AtRuleKind::Import) => {
|
||||||
@ -479,7 +491,9 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
|
|
||||||
let (new_rules, new_scope) = import(file_name)?;
|
let (new_rules, new_scope) = import(file_name)?;
|
||||||
rules.extend(new_rules);
|
rules.extend(new_rules);
|
||||||
self.global_scope.extend(new_scope);
|
GLOBAL_SCOPE.with(|s| {
|
||||||
|
s.borrow_mut().extend(new_scope);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
TokenKind::AtRule(_) => {
|
TokenKind::AtRule(_) => {
|
||||||
if let Some(Token {
|
if let Some(Token {
|
||||||
@ -487,12 +501,16 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
pos,
|
pos,
|
||||||
}) = self.lexer.next()
|
}) = self.lexer.next()
|
||||||
{
|
{
|
||||||
match AtRule::from_tokens(rule, pos, &mut self.lexer, &mut self.global_scope, &Selector::new())? {
|
match AtRule::from_tokens(rule, pos, &mut self.lexer, &mut GLOBAL_SCOPE.with(|s| s.borrow().clone()), &Selector::new())? {
|
||||||
AtRule::Mixin(name, mixin) => {
|
AtRule::Mixin(name, mixin) => {
|
||||||
self.global_scope.insert_mixin(&name, *mixin);
|
GLOBAL_SCOPE.with(|s| {
|
||||||
|
s.borrow_mut().insert_mixin(&name, *mixin);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
AtRule::Function(name, func) => {
|
AtRule::Function(name, func) => {
|
||||||
self.global_scope.insert_fn(&name, *func);
|
GLOBAL_SCOPE.with(|s| {
|
||||||
|
s.borrow_mut().insert_fn(&name, *func);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
AtRule::Charset => continue,
|
AtRule::Charset => continue,
|
||||||
AtRule::Error(pos, message) => self.error(pos, &message),
|
AtRule::Error(pos, message) => self.error(pos, &message),
|
||||||
@ -518,7 +536,7 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Ok((rules, self.global_scope))
|
Ok((rules, GLOBAL_SCOPE.with(|s| s.borrow().clone())))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat_rules(&mut self, super_selector: &Selector, scope: &mut Scope) -> SassResult<Vec<Stmt>> {
|
fn eat_rules(&mut self, super_selector: &Selector, scope: &mut Scope) -> SassResult<Vec<Stmt>> {
|
||||||
@ -556,7 +574,7 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
Expr::VariableDecl(name, val) => {
|
Expr::VariableDecl(name, val) => {
|
||||||
if self.scope == 0 {
|
if self.scope == 0 {
|
||||||
scope.insert_var(&name, *val.clone())?;
|
scope.insert_var(&name, *val.clone())?;
|
||||||
self.global_scope.insert_var(&name, *val)?;
|
insert_global_var(&name, *val)?;
|
||||||
} else {
|
} else {
|
||||||
scope.insert_var(&name, *val)?;
|
scope.insert_var(&name, *val)?;
|
||||||
}
|
}
|
||||||
@ -648,9 +666,14 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
|||||||
{
|
{
|
||||||
toks.next();
|
toks.next();
|
||||||
devour_whitespace(toks);
|
devour_whitespace(toks);
|
||||||
let VariableDecl { val, default } =
|
let VariableDecl {
|
||||||
eat_variable_value(toks, scope, super_selector)?;
|
val,
|
||||||
if !default || scope.get_var(&name).is_err() {
|
default,
|
||||||
|
global,
|
||||||
|
} = eat_variable_value(toks, scope, super_selector)?;
|
||||||
|
if global {
|
||||||
|
insert_global_var(&name, val)?;
|
||||||
|
} else if !default || scope.get_var(&name).is_err() {
|
||||||
return Ok(Some(Expr::VariableDecl(name, Box::new(val))));
|
return Ok(Some(Expr::VariableDecl(name, Box::new(val))));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -3,8 +3,9 @@ use std::vec::IntoIter;
|
|||||||
|
|
||||||
use crate::args::{eat_call_args, eat_func_args, CallArgs, FuncArgs};
|
use crate::args::{eat_call_args, eat_func_args, CallArgs, FuncArgs};
|
||||||
use crate::atrule::AtRule;
|
use crate::atrule::AtRule;
|
||||||
use crate::common::{Scope, Symbol};
|
use crate::common::Symbol;
|
||||||
use crate::error::{SassError, SassResult};
|
use crate::error::{SassError, SassResult};
|
||||||
|
use crate::scope::Scope;
|
||||||
use crate::selector::Selector;
|
use crate::selector::Selector;
|
||||||
use crate::utils::devour_whitespace;
|
use crate::utils::devour_whitespace;
|
||||||
use crate::{eat_expr, Expr, RuleSet, Stmt, Token, TokenKind};
|
use crate::{eat_expr, Expr, RuleSet, Stmt, Token, TokenKind};
|
||||||
|
94
src/scope.rs
Normal file
94
src/scope.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::error::SassResult;
|
||||||
|
use crate::function::Function;
|
||||||
|
use crate::mixin::Mixin;
|
||||||
|
use crate::value::Value;
|
||||||
|
|
||||||
|
thread_local!(pub(crate) static GLOBAL_SCOPE: RefCell<Scope> = RefCell::new(Scope::new()));
|
||||||
|
|
||||||
|
pub(crate) fn get_global_var(s: &str) -> SassResult<Value> {
|
||||||
|
GLOBAL_SCOPE.with(|scope| match scope.borrow().vars().get(s) {
|
||||||
|
Some(v) => Ok(v.clone()),
|
||||||
|
None => Err("Undefined variable.".into()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn insert_global_var(s: &str, v: Value) -> SassResult<Option<Value>> {
|
||||||
|
GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_var(s, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct Scope {
|
||||||
|
vars: HashMap<String, Value>,
|
||||||
|
mixins: HashMap<String, Mixin>,
|
||||||
|
functions: HashMap<String, Function>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scope {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
vars: HashMap::new(),
|
||||||
|
mixins: HashMap::new(),
|
||||||
|
functions: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vars(&self) -> &HashMap<String, Value> {
|
||||||
|
&self.vars
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_var(&self, v: &str) -> SassResult<Value> {
|
||||||
|
let v = &v.replace('_', "-");
|
||||||
|
match self.vars.get(v) {
|
||||||
|
Some(v) => Ok(v.clone()),
|
||||||
|
None => get_global_var(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_var(&mut self, s: &str, v: Value) -> SassResult<Option<Value>> {
|
||||||
|
Ok(self.vars.insert(s.replace('_', "-"), v.eval()?))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn var_exists(&self, v: &str) -> bool {
|
||||||
|
self.vars.contains_key(&v.replace('_', "-"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mixin(&self, v: &str) -> SassResult<&Mixin> {
|
||||||
|
match self.mixins.get(&v.replace('_', "-")) {
|
||||||
|
Some(v) => Ok(v),
|
||||||
|
None => Err("Undefined mixin.".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_mixin(&mut self, s: &str, v: Mixin) -> Option<Mixin> {
|
||||||
|
self.mixins.insert(s.replace('_', "-"), v)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mixin_exists(&self, v: &str) -> bool {
|
||||||
|
self.mixins.contains_key(&v.replace('_', "-"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fn(&self, v: &str) -> SassResult<&Function> {
|
||||||
|
match self.functions.get(&v.replace('_', "-")) {
|
||||||
|
Some(v) => Ok(v),
|
||||||
|
None => Err("Undefined function.".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_fn(&mut self, s: &str, v: Function) -> Option<Function> {
|
||||||
|
self.functions.insert(s.replace('_', "-"), v)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fn_exists(&self, v: &str) -> bool {
|
||||||
|
self.functions.contains_key(&v.replace('_', "-"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend(&mut self, other: Scope) {
|
||||||
|
self.vars.extend(other.vars);
|
||||||
|
self.mixins.extend(other.mixins);
|
||||||
|
self.functions.extend(other.functions);
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,10 @@ use std::fmt::{self, Display, Write};
|
|||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
|
||||||
use crate::common::{Scope, Symbol, Whitespace};
|
use crate::common::{Symbol, Whitespace};
|
||||||
use crate::error::SassResult;
|
use crate::error::SassResult;
|
||||||
use crate::lexer::Lexer;
|
use crate::lexer::Lexer;
|
||||||
|
use crate::scope::Scope;
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
devour_whitespace, devour_whitespace_or_comment, flatten_ident, parse_interpolation,
|
devour_whitespace, devour_whitespace_or_comment, flatten_ident, parse_interpolation,
|
||||||
parse_quoted_string, IsWhitespace,
|
parse_quoted_string, IsWhitespace,
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use crate::common::{Pos, QuoteKind, Scope, Symbol};
|
use crate::common::{Pos, QuoteKind, Symbol};
|
||||||
use crate::error::SassResult;
|
use crate::error::SassResult;
|
||||||
|
use crate::scope::Scope;
|
||||||
use crate::selector::Selector;
|
use crate::selector::Selector;
|
||||||
use crate::utils::{devour_whitespace, parse_interpolation, parse_quoted_string};
|
use crate::utils::{devour_whitespace, parse_interpolation, parse_quoted_string};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
16
src/utils.rs
16
src/utils.rs
@ -83,11 +83,16 @@ pub(crate) fn parse_interpolation<I: Iterator<Item = Token>>(
|
|||||||
pub(crate) struct VariableDecl {
|
pub(crate) struct VariableDecl {
|
||||||
pub val: Value,
|
pub val: Value,
|
||||||
pub default: bool,
|
pub default: bool,
|
||||||
|
pub global: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VariableDecl {
|
impl VariableDecl {
|
||||||
pub const fn new(val: Value, default: bool) -> VariableDecl {
|
pub const fn new(val: Value, default: bool, global: bool) -> VariableDecl {
|
||||||
VariableDecl { val, default }
|
VariableDecl {
|
||||||
|
val,
|
||||||
|
default,
|
||||||
|
global,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,6 +103,7 @@ pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
|
|||||||
) -> SassResult<VariableDecl> {
|
) -> SassResult<VariableDecl> {
|
||||||
devour_whitespace(toks);
|
devour_whitespace(toks);
|
||||||
let mut default = false;
|
let mut default = false;
|
||||||
|
let mut global = false;
|
||||||
let mut raw: Vec<Token> = Vec::new();
|
let mut raw: Vec<Token> = Vec::new();
|
||||||
let mut nesting = 0;
|
let mut nesting = 0;
|
||||||
while let Some(tok) = toks.peek() {
|
while let Some(tok) = toks.peek() {
|
||||||
@ -110,6 +116,10 @@ pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
|
|||||||
toks.next();
|
toks.next();
|
||||||
default = true
|
default = true
|
||||||
}
|
}
|
||||||
|
TokenKind::Keyword(Keyword::Global) => {
|
||||||
|
toks.next();
|
||||||
|
global = true
|
||||||
|
}
|
||||||
TokenKind::Interpolation | TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
TokenKind::Interpolation | TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
||||||
nesting += 1;
|
nesting += 1;
|
||||||
raw.push(toks.next().unwrap());
|
raw.push(toks.next().unwrap());
|
||||||
@ -127,7 +137,7 @@ pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
|
|||||||
}
|
}
|
||||||
devour_whitespace(toks);
|
devour_whitespace(toks);
|
||||||
let val = Value::from_tokens(&mut raw.into_iter().peekable(), scope, super_selector).unwrap();
|
let val = Value::from_tokens(&mut raw.into_iter().peekable(), scope, super_selector).unwrap();
|
||||||
Ok(VariableDecl::new(val, default))
|
Ok(VariableDecl::new(val, default, global))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn flatten_ident<I: Iterator<Item = Token>>(
|
pub(crate) fn flatten_ident<I: Iterator<Item = Token>>(
|
||||||
|
@ -8,8 +8,9 @@ use num_traits::pow;
|
|||||||
use crate::args::eat_call_args;
|
use crate::args::eat_call_args;
|
||||||
use crate::builtin::GLOBAL_FUNCTIONS;
|
use crate::builtin::GLOBAL_FUNCTIONS;
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::common::{Keyword, ListSeparator, Op, QuoteKind, Scope, Symbol};
|
use crate::common::{Keyword, ListSeparator, Op, QuoteKind, Symbol};
|
||||||
use crate::error::SassResult;
|
use crate::error::SassResult;
|
||||||
|
use crate::scope::Scope;
|
||||||
use crate::selector::Selector;
|
use crate::selector::Selector;
|
||||||
use crate::units::Unit;
|
use crate::units::Unit;
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
|
15
tests/scope.rs
Normal file
15
tests/scope.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
|
test!(
|
||||||
|
scoping_var_decl_inner_ruleset,
|
||||||
|
"a {\n $color: red;\n b {\n $color: blue;\n }\n color: $color;\n}\n",
|
||||||
|
"a {\n color: blue;\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
basic_global,
|
||||||
|
"a {\n $color: red !global;\n}\n\nb {\n color: $color;\n}\n",
|
||||||
|
"b {\n color: red;\n}\n"
|
||||||
|
);
|
Loading…
x
Reference in New Issue
Block a user