2020-07-08 20:50:18 -04:00
|
|
|
use std::collections::BTreeMap;
|
2020-03-17 20:13:53 -04:00
|
|
|
|
2020-04-12 19:37:12 -04:00
|
|
|
use codemap::Spanned;
|
|
|
|
|
2020-06-16 20:00:11 -04:00
|
|
|
use crate::{
|
2020-07-27 18:55:38 -04:00
|
|
|
atrule::Mixin,
|
|
|
|
builtin::{modules::Module, GLOBAL_FUNCTIONS},
|
2020-06-16 20:00:11 -04:00
|
|
|
common::Identifier,
|
|
|
|
error::SassResult,
|
2020-07-27 18:55:38 -04:00
|
|
|
value::{SassFunction, Value},
|
2020-06-16 20:00:11 -04:00
|
|
|
};
|
2020-03-17 20:13:53 -04:00
|
|
|
|
2020-07-08 20:26:54 -04:00
|
|
|
#[derive(Debug, Default)]
|
2020-03-17 20:13:53 -04:00
|
|
|
pub(crate) struct Scope {
|
2020-07-30 17:21:32 -04:00
|
|
|
pub vars: BTreeMap<Identifier, Value>,
|
|
|
|
pub mixins: BTreeMap<Identifier, Mixin>,
|
|
|
|
pub functions: BTreeMap<Identifier, SassFunction>,
|
2020-03-17 20:13:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Scope {
|
2020-07-08 22:38:56 -04:00
|
|
|
// `BTreeMap::new` is not yet const
|
|
|
|
#[allow(clippy::missing_const_for_fn)]
|
2020-03-17 20:13:53 -04:00
|
|
|
#[must_use]
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
2020-07-08 20:50:18 -04:00
|
|
|
vars: BTreeMap::new(),
|
|
|
|
mixins: BTreeMap::new(),
|
|
|
|
functions: BTreeMap::new(),
|
2020-03-17 20:13:53 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-08 22:38:56 -04:00
|
|
|
fn get_var(&self, name: Spanned<Identifier>) -> SassResult<&Value> {
|
|
|
|
match self.vars.get(&name.node) {
|
2020-07-27 18:55:38 -04:00
|
|
|
Some(v) => Ok(v),
|
2020-06-16 19:38:30 -04:00
|
|
|
None => Err(("Undefined variable.", name.span).into()),
|
|
|
|
}
|
2020-04-04 14:53:08 -04:00
|
|
|
}
|
|
|
|
|
2020-07-27 18:55:38 -04:00
|
|
|
pub fn insert_var(&mut self, s: Identifier, v: Value) -> Option<Value> {
|
2020-07-07 17:22:18 -04:00
|
|
|
self.vars.insert(s, v)
|
2020-03-17 20:13:53 -04:00
|
|
|
}
|
|
|
|
|
2020-07-08 22:38:56 -04:00
|
|
|
pub fn var_exists(&self, name: Identifier) -> bool {
|
|
|
|
self.vars.contains_key(&name)
|
2020-06-16 19:38:30 -04:00
|
|
|
}
|
|
|
|
|
2020-07-08 22:38:56 -04:00
|
|
|
fn get_mixin(&self, name: Spanned<Identifier>) -> SassResult<Mixin> {
|
|
|
|
match self.mixins.get(&name.node) {
|
2020-06-16 19:38:30 -04:00
|
|
|
Some(v) => Ok(v.clone()),
|
|
|
|
None => Err(("Undefined mixin.", name.span).into()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-22 18:21:18 -04:00
|
|
|
pub fn insert_mixin<T: Into<Identifier>>(&mut self, s: T, v: Mixin) -> Option<Mixin> {
|
|
|
|
self.mixins.insert(s.into(), v)
|
2020-03-17 20:13:53 -04:00
|
|
|
}
|
|
|
|
|
2020-07-08 22:38:56 -04:00
|
|
|
fn mixin_exists(&self, name: Identifier) -> bool {
|
|
|
|
self.mixins.contains_key(&name)
|
2020-06-16 19:38:30 -04:00
|
|
|
}
|
|
|
|
|
2020-07-27 18:55:38 -04:00
|
|
|
fn get_fn(&self, name: Identifier) -> Option<SassFunction> {
|
2020-07-08 22:38:56 -04:00
|
|
|
self.functions.get(&name).cloned()
|
2020-03-17 20:13:53 -04:00
|
|
|
}
|
|
|
|
|
2020-07-27 18:55:38 -04:00
|
|
|
pub fn insert_fn(&mut self, s: Identifier, v: SassFunction) -> Option<SassFunction> {
|
|
|
|
self.functions.insert(s, v)
|
2020-03-17 20:13:53 -04:00
|
|
|
}
|
|
|
|
|
2020-07-08 22:38:56 -04:00
|
|
|
fn fn_exists(&self, name: Identifier) -> bool {
|
2020-07-08 21:01:05 -04:00
|
|
|
if self.functions.is_empty() {
|
|
|
|
return false;
|
|
|
|
}
|
2020-07-08 22:38:56 -04:00
|
|
|
self.functions.contains_key(&name)
|
2020-06-16 19:38:30 -04:00
|
|
|
}
|
|
|
|
|
2020-07-08 14:51:04 -04:00
|
|
|
fn merge(&mut self, other: Scope) {
|
|
|
|
self.vars.extend(other.vars);
|
|
|
|
self.mixins.extend(other.mixins);
|
|
|
|
self.functions.extend(other.functions);
|
|
|
|
}
|
2020-07-27 18:55:38 -04:00
|
|
|
|
|
|
|
pub fn merge_module(&mut self, other: Module) {
|
2020-07-30 17:21:32 -04:00
|
|
|
self.merge(other.0);
|
2020-07-27 18:55:38 -04:00
|
|
|
}
|
2020-07-08 14:51:04 -04:00
|
|
|
}
|
|
|
|
|
2020-07-08 20:26:54 -04:00
|
|
|
#[derive(Debug, Default)]
|
2020-07-08 14:51:04 -04:00
|
|
|
pub(crate) struct Scopes(Vec<Scope>);
|
|
|
|
|
|
|
|
impl Scopes {
|
|
|
|
pub const fn new() -> Self {
|
|
|
|
Self(Vec::new())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn enter_new_scope(&mut self) {
|
|
|
|
self.0.push(Scope::new());
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn enter_scope(&mut self, scope: Scope) {
|
|
|
|
self.0.push(scope);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn exit_scope(&mut self) {
|
|
|
|
self.0.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn merge(&mut self, other: Scope) {
|
|
|
|
if let Some(scope) = self.0.last_mut() {
|
|
|
|
scope.merge(other)
|
|
|
|
} else {
|
|
|
|
panic!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Variables
|
|
|
|
impl Scopes {
|
2020-07-27 18:55:38 -04:00
|
|
|
pub fn insert_var(&mut self, s: Identifier, v: Value) -> Option<Value> {
|
2020-07-08 14:51:04 -04:00
|
|
|
for scope in self.0.iter_mut().rev() {
|
2020-07-08 22:38:56 -04:00
|
|
|
if scope.var_exists(s) {
|
2020-07-08 14:51:04 -04:00
|
|
|
return scope.insert_var(s, v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(scope) = self.0.last_mut() {
|
|
|
|
scope.insert_var(s, v)
|
2020-07-08 23:12:03 -04:00
|
|
|
} else {
|
|
|
|
let mut scope = Scope::new();
|
|
|
|
scope.insert_var(s, v);
|
|
|
|
self.0.push(scope);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Always insert this variable into the innermost scope
|
|
|
|
///
|
|
|
|
/// Used, for example, for variables from `@each` and `@for`
|
2020-07-27 18:55:38 -04:00
|
|
|
pub fn insert_var_last(&mut self, s: Identifier, v: Value) -> Option<Value> {
|
2020-07-08 23:12:03 -04:00
|
|
|
if let Some(scope) = self.0.last_mut() {
|
|
|
|
scope.insert_var(s, v)
|
2020-07-08 14:51:04 -04:00
|
|
|
} else {
|
|
|
|
let mut scope = Scope::new();
|
|
|
|
scope.insert_var(s, v);
|
|
|
|
self.0.push(scope);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-27 18:55:38 -04:00
|
|
|
pub fn insert_default_var(&mut self, s: Identifier, v: Value) -> Option<Value> {
|
2020-07-08 14:51:04 -04:00
|
|
|
if let Some(scope) = self.0.last_mut() {
|
2020-07-08 22:38:56 -04:00
|
|
|
if scope.var_exists(s) {
|
2020-07-08 14:51:04 -04:00
|
|
|
None
|
|
|
|
} else {
|
|
|
|
scope.insert_var(s, v)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
panic!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_var<'a>(
|
|
|
|
&'a self,
|
2020-07-08 22:38:56 -04:00
|
|
|
name: Spanned<Identifier>,
|
2020-07-08 14:51:04 -04:00
|
|
|
global_scope: &'a Scope,
|
|
|
|
) -> SassResult<&Value> {
|
|
|
|
for scope in self.0.iter().rev() {
|
2020-07-08 22:38:56 -04:00
|
|
|
if scope.var_exists(name.node) {
|
2020-07-08 20:26:54 -04:00
|
|
|
return scope.get_var(name);
|
2020-07-08 14:51:04 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
global_scope.get_var(name)
|
|
|
|
}
|
|
|
|
|
2020-07-08 22:38:56 -04:00
|
|
|
pub fn var_exists(&self, name: Identifier, global_scope: &Scope) -> bool {
|
2020-07-08 14:51:04 -04:00
|
|
|
for scope in &self.0 {
|
|
|
|
if scope.var_exists(name) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
global_scope.var_exists(name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Mixins
|
|
|
|
impl Scopes {
|
|
|
|
pub fn insert_mixin(&mut self, s: Identifier, v: Mixin) -> Option<Mixin> {
|
|
|
|
if let Some(scope) = self.0.last_mut() {
|
|
|
|
scope.insert_mixin(s, v)
|
|
|
|
} else {
|
|
|
|
let mut scope = Scope::new();
|
|
|
|
scope.insert_mixin(s, v);
|
|
|
|
self.0.push(scope);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_mixin<'a>(
|
|
|
|
&'a self,
|
2020-07-08 22:38:56 -04:00
|
|
|
name: Spanned<Identifier>,
|
2020-07-08 14:51:04 -04:00
|
|
|
global_scope: &'a Scope,
|
|
|
|
) -> SassResult<Mixin> {
|
|
|
|
for scope in self.0.iter().rev() {
|
2020-07-08 22:38:56 -04:00
|
|
|
if scope.mixin_exists(name.node) {
|
2020-07-08 20:26:54 -04:00
|
|
|
return scope.get_mixin(name);
|
2020-07-08 14:51:04 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
global_scope.get_mixin(name)
|
|
|
|
}
|
|
|
|
|
2020-07-08 22:38:56 -04:00
|
|
|
pub fn mixin_exists(&self, name: Identifier, global_scope: &Scope) -> bool {
|
2020-07-08 14:51:04 -04:00
|
|
|
for scope in &self.0 {
|
|
|
|
if scope.mixin_exists(name) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
global_scope.mixin_exists(name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Functions
|
|
|
|
impl Scopes {
|
2020-07-27 18:55:38 -04:00
|
|
|
pub fn insert_fn(&mut self, s: Identifier, v: SassFunction) -> Option<SassFunction> {
|
2020-07-08 14:51:04 -04:00
|
|
|
if let Some(scope) = self.0.last_mut() {
|
|
|
|
scope.insert_fn(s, v)
|
|
|
|
} else {
|
|
|
|
let mut scope = Scope::new();
|
|
|
|
scope.insert_fn(s, v);
|
|
|
|
self.0.push(scope);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_fn<'a>(
|
|
|
|
&'a self,
|
2020-07-08 22:38:56 -04:00
|
|
|
name: Spanned<Identifier>,
|
2020-07-08 14:51:04 -04:00
|
|
|
global_scope: &'a Scope,
|
2020-07-27 18:55:38 -04:00
|
|
|
) -> Option<SassFunction> {
|
2020-07-08 14:51:04 -04:00
|
|
|
for scope in self.0.iter().rev() {
|
2020-07-08 22:38:56 -04:00
|
|
|
if scope.fn_exists(name.node) {
|
2020-07-08 20:35:15 -04:00
|
|
|
return scope.get_fn(name.node);
|
2020-07-08 14:51:04 -04:00
|
|
|
}
|
|
|
|
}
|
2020-07-08 20:35:15 -04:00
|
|
|
global_scope.get_fn(name.node)
|
2020-07-08 14:51:04 -04:00
|
|
|
}
|
|
|
|
|
2020-07-08 22:38:56 -04:00
|
|
|
pub fn fn_exists(&self, name: Identifier, global_scope: &Scope) -> bool {
|
2020-07-08 14:51:04 -04:00
|
|
|
for scope in &self.0 {
|
|
|
|
if scope.fn_exists(name) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
global_scope.fn_exists(name) || GLOBAL_FUNCTIONS.contains_key(name.as_str())
|
2020-03-17 20:13:53 -04:00
|
|
|
}
|
|
|
|
}
|