create structure for identifiers
it looks like rustfmt also updated during this commit, so there are some formatting changes
This commit is contained in:
parent
283a2097ff
commit
2102781f62
@ -42,7 +42,7 @@ impl Each {
|
||||
if self.vars.len() == 1 {
|
||||
if this_iterator.len() == 1 {
|
||||
scope.insert_var(
|
||||
&self.vars[0],
|
||||
&self.vars[0].node,
|
||||
Spanned {
|
||||
node: this_iterator[0].clone(),
|
||||
span: self.vars[0].span,
|
||||
@ -50,7 +50,7 @@ impl Each {
|
||||
)?;
|
||||
} else {
|
||||
scope.insert_var(
|
||||
&self.vars[0],
|
||||
&self.vars[0].node,
|
||||
Spanned {
|
||||
node: Value::List(this_iterator, ListSeparator::Space, Brackets::None),
|
||||
span: self.vars[0].span,
|
||||
|
@ -119,3 +119,30 @@ impl Display for QualifiedName {
|
||||
f.write_str(&self.ident)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub(crate) struct Identifier(String);
|
||||
|
||||
impl From<String> for Identifier {
|
||||
fn from(s: String) -> Identifier {
|
||||
Identifier(s.replace('_', "-"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&String> for Identifier {
|
||||
fn from(s: &String) -> Identifier {
|
||||
Identifier(s.replace('_', "-"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Identifier {
|
||||
fn from(s: &str) -> Identifier {
|
||||
Identifier(s.replace('_', "-"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Identifier {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
107
src/scope.rs
107
src/scope.rs
@ -4,66 +4,67 @@ use std::collections::HashMap;
|
||||
use codemap::Spanned;
|
||||
|
||||
use crate::atrule::{Function, Mixin};
|
||||
use crate::common::Identifier;
|
||||
use crate::error::SassResult;
|
||||
use crate::value::Value;
|
||||
|
||||
thread_local!(pub(crate) static GLOBAL_SCOPE: RefCell<Scope> = RefCell::new(Scope::new()));
|
||||
|
||||
pub(crate) fn get_global_var(s: Spanned<String>) -> SassResult<Spanned<Value>> {
|
||||
GLOBAL_SCOPE.with(|scope| match scope.borrow().vars().get(&s.node) {
|
||||
pub(crate) fn get_global_var<T: Into<Identifier>>(s: Spanned<T>) -> SassResult<Spanned<Value>> {
|
||||
GLOBAL_SCOPE.with(|scope| match scope.borrow().vars().get(&s.node.into()) {
|
||||
Some(v) => Ok(v.clone()),
|
||||
None => Err(("Undefined variable.", s.span).into()),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn global_var_exists(v: &str) -> bool {
|
||||
GLOBAL_SCOPE.with(|scope| scope.borrow().vars().contains_key(&v.replace('_', "-")))
|
||||
pub(crate) fn global_var_exists<T: Into<Identifier>>(v: T) -> bool {
|
||||
GLOBAL_SCOPE.with(|scope| scope.borrow().vars().contains_key(&v.into()))
|
||||
}
|
||||
|
||||
pub(crate) fn insert_global_var(s: &str, v: Spanned<Value>) -> SassResult<Option<Spanned<Value>>> {
|
||||
GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_var(s, v))
|
||||
pub(crate) fn insert_global_var<T: Into<Identifier>>(
|
||||
s: T,
|
||||
v: Spanned<Value>,
|
||||
) -> SassResult<Option<Spanned<Value>>> {
|
||||
GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_var(s.into(), v))
|
||||
}
|
||||
|
||||
pub(crate) fn get_global_fn(s: Spanned<String>) -> SassResult<Function> {
|
||||
GLOBAL_SCOPE.with(|scope| match scope.borrow().functions().get(&s.node) {
|
||||
pub(crate) fn get_global_fn<T: Into<Identifier>>(s: Spanned<T>) -> SassResult<Function> {
|
||||
GLOBAL_SCOPE.with(
|
||||
|scope| match scope.borrow().functions().get(&s.node.into()) {
|
||||
Some(v) => Ok(v.clone()),
|
||||
None => Err(("Undefined function.", s.span).into()),
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn global_fn_exists(v: &str) -> bool {
|
||||
GLOBAL_SCOPE.with(|scope| {
|
||||
scope
|
||||
.borrow()
|
||||
.functions()
|
||||
.contains_key(&v.replace('_', "-"))
|
||||
})
|
||||
pub(crate) fn global_fn_exists<T: Into<Identifier>>(v: T) -> bool {
|
||||
GLOBAL_SCOPE.with(|scope| scope.borrow().functions().contains_key(&v.into()))
|
||||
}
|
||||
|
||||
pub(crate) fn insert_global_fn(s: &str, v: Function) -> Option<Function> {
|
||||
GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_fn(s, v))
|
||||
pub(crate) fn insert_global_fn<T: Into<Identifier>>(s: T, v: Function) -> Option<Function> {
|
||||
GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_fn(s.into(), v))
|
||||
}
|
||||
|
||||
pub(crate) fn get_global_mixin(s: Spanned<String>) -> SassResult<Mixin> {
|
||||
GLOBAL_SCOPE.with(|scope| match scope.borrow().mixins().get(&s.node) {
|
||||
pub(crate) fn get_global_mixin<T: Into<Identifier>>(s: Spanned<T>) -> SassResult<Mixin> {
|
||||
GLOBAL_SCOPE.with(|scope| match scope.borrow().mixins().get(&s.node.into()) {
|
||||
Some(v) => Ok(v.clone()),
|
||||
None => Err(("Undefined mixin.", s.span).into()),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn global_mixin_exists(v: &str) -> bool {
|
||||
GLOBAL_SCOPE.with(|scope| scope.borrow().mixins().contains_key(&v.replace('_', "-")))
|
||||
pub(crate) fn global_mixin_exists<T: Into<Identifier>>(v: T) -> bool {
|
||||
GLOBAL_SCOPE.with(|scope| scope.borrow().mixins().contains_key(&v.into()))
|
||||
}
|
||||
|
||||
pub(crate) fn insert_global_mixin(s: &str, v: Mixin) -> Option<Mixin> {
|
||||
GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_mixin(s, v))
|
||||
pub(crate) fn insert_global_mixin<T: Into<Identifier>>(s: T, v: Mixin) -> Option<Mixin> {
|
||||
GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_mixin(s.into(), v))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Scope {
|
||||
vars: HashMap<String, Spanned<Value>>,
|
||||
mixins: HashMap<String, Mixin>,
|
||||
functions: HashMap<String, Function>,
|
||||
vars: HashMap<Identifier, Spanned<Value>>,
|
||||
mixins: HashMap<Identifier, Mixin>,
|
||||
functions: HashMap<Identifier, Function>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
@ -76,68 +77,72 @@ impl Scope {
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn vars(&self) -> &HashMap<String, Spanned<Value>> {
|
||||
pub const fn vars(&self) -> &HashMap<Identifier, Spanned<Value>> {
|
||||
&self.vars
|
||||
}
|
||||
|
||||
pub const fn functions(&self) -> &HashMap<String, Function> {
|
||||
pub const fn functions(&self) -> &HashMap<Identifier, Function> {
|
||||
&self.functions
|
||||
}
|
||||
|
||||
pub const fn mixins(&self) -> &HashMap<String, Mixin> {
|
||||
pub const fn mixins(&self) -> &HashMap<Identifier, Mixin> {
|
||||
&self.mixins
|
||||
}
|
||||
|
||||
pub fn get_var(&self, mut name: Spanned<String>) -> SassResult<Spanned<Value>> {
|
||||
name.node = name.node.replace('_', "-");
|
||||
pub fn get_var<T: Into<Identifier>>(&self, name: Spanned<T>) -> SassResult<Spanned<Value>> {
|
||||
let name = name.map_node(|n| n.into());
|
||||
match self.vars.get(&name.node) {
|
||||
Some(v) => Ok(v.clone()),
|
||||
None => get_global_var(name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_var(&mut self, s: &str, v: Spanned<Value>) -> SassResult<Option<Spanned<Value>>> {
|
||||
pub fn insert_var<T: Into<Identifier>>(
|
||||
&mut self,
|
||||
s: T,
|
||||
v: Spanned<Value>,
|
||||
) -> SassResult<Option<Spanned<Value>>> {
|
||||
let Spanned { node, span } = v;
|
||||
Ok(self.vars.insert(s.replace('_', "-"), node.eval(span)?))
|
||||
Ok(self.vars.insert(s.into(), node.eval(span)?))
|
||||
}
|
||||
|
||||
pub fn var_exists(&self, v: &str) -> bool {
|
||||
let name = &v.replace('_', "-");
|
||||
self.vars.contains_key(name) || global_var_exists(name)
|
||||
pub fn var_exists<T: Into<Identifier>>(&self, v: T) -> bool {
|
||||
let name = v.into();
|
||||
self.vars.contains_key(&name) || global_var_exists(name)
|
||||
}
|
||||
|
||||
pub fn get_mixin(&self, mut name: Spanned<String>) -> SassResult<Mixin> {
|
||||
name.node = name.node.replace('_', "-");
|
||||
pub fn get_mixin<T: Into<Identifier>>(&self, name: Spanned<T>) -> SassResult<Mixin> {
|
||||
let name = name.map_node(|n| n.into());
|
||||
match self.mixins.get(&name.node) {
|
||||
Some(v) => Ok(v.clone()),
|
||||
None => get_global_mixin(name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_mixin(&mut self, s: &str, v: Mixin) -> Option<Mixin> {
|
||||
self.mixins.insert(s.replace('_', "-"), v)
|
||||
pub fn insert_mixin<T: Into<Identifier>>(&mut self, s: T, v: Mixin) -> Option<Mixin> {
|
||||
self.mixins.insert(s.into(), v)
|
||||
}
|
||||
|
||||
pub fn mixin_exists(&self, v: &str) -> bool {
|
||||
let name = &v.replace('_', "-");
|
||||
self.mixins.contains_key(name) || global_mixin_exists(name)
|
||||
pub fn mixin_exists<T: Into<Identifier>>(&self, v: T) -> bool {
|
||||
let name = v.into();
|
||||
self.mixins.contains_key(&name) || global_mixin_exists(name)
|
||||
}
|
||||
|
||||
pub fn get_fn(&self, mut name: Spanned<String>) -> SassResult<Function> {
|
||||
name.node = name.node.replace('_', "-");
|
||||
pub fn get_fn<T: Into<Identifier>>(&self, name: Spanned<T>) -> SassResult<Function> {
|
||||
let name = name.map_node(|n| n.into());
|
||||
match self.functions.get(&name.node) {
|
||||
Some(v) => Ok(v.clone()),
|
||||
None => get_global_fn(name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_fn(&mut self, s: &str, v: Function) -> Option<Function> {
|
||||
self.functions.insert(s.replace('_', "-"), v)
|
||||
pub fn insert_fn<T: Into<Identifier>>(&mut self, s: T, v: Function) -> Option<Function> {
|
||||
self.functions.insert(s.into(), v)
|
||||
}
|
||||
|
||||
pub fn fn_exists(&self, v: &str) -> bool {
|
||||
let name = &v.replace('_', "-");
|
||||
self.functions.contains_key(name) || global_fn_exists(name)
|
||||
pub fn fn_exists<T: Into<Identifier>>(&self, v: T) -> bool {
|
||||
let name = v.into();
|
||||
self.functions.contains_key(&name) || global_fn_exists(name)
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, other: Scope) {
|
||||
|
@ -164,8 +164,9 @@ impl<'a> StyleSheetParser<'a> {
|
||||
let mut rules: Vec<Spanned<Stmt>> = Vec::new();
|
||||
while let Some(Token { kind, .. }) = self.lexer.peek() {
|
||||
match kind {
|
||||
_ if is_selector_char(*kind) => rules
|
||||
.extend(self.eat_rules(&Selector::new(), &mut Scope::new())?),
|
||||
_ if is_selector_char(*kind) => {
|
||||
rules.extend(self.eat_rules(&Selector::new(), &mut Scope::new())?)
|
||||
}
|
||||
'\t' | '\n' | ' ' => {
|
||||
self.lexer.next();
|
||||
continue;
|
||||
@ -177,14 +178,15 @@ impl<'a> StyleSheetParser<'a> {
|
||||
|
||||
match self.lexer.peek() {
|
||||
Some(Token { kind: ':', .. }) => {
|
||||
self.lexer.take(name.node.chars().count() + whitespace + 1)
|
||||
self.lexer
|
||||
.take(name.node.chars().count() + whitespace + 1)
|
||||
.for_each(drop);
|
||||
devour_whitespace(self.lexer);
|
||||
|
||||
let VariableDecl { val, default, .. } =
|
||||
eat_variable_value(self.lexer, &Scope::new(), &Selector::new())?;
|
||||
|
||||
if !(default && global_var_exists(&name)) {
|
||||
if !(default && global_var_exists(&name.node)) {
|
||||
insert_global_var(&name.node, val)?;
|
||||
}
|
||||
}
|
||||
@ -202,17 +204,15 @@ impl<'a> StyleSheetParser<'a> {
|
||||
let comment = eat_comment(self.lexer, &Scope::new(), &Selector::new())?;
|
||||
rules.push(comment.map_node(Stmt::MultilineComment));
|
||||
}
|
||||
_ => return Err(("expected selector.", pos).into())
|
||||
_ => return Err(("expected selector.", pos).into()),
|
||||
}
|
||||
}
|
||||
'@' => {
|
||||
let span_before = self.lexer.next().unwrap().pos();
|
||||
let Spanned { node: at_rule_kind, span } = eat_ident(
|
||||
self.lexer,
|
||||
&Scope::new(),
|
||||
&Selector::new(),
|
||||
span_before
|
||||
)?;
|
||||
let Spanned {
|
||||
node: at_rule_kind,
|
||||
span,
|
||||
} = eat_ident(self.lexer, &Scope::new(), &Selector::new(), span_before)?;
|
||||
if at_rule_kind.is_empty() {
|
||||
return Err(("Expected identifier.", span).into());
|
||||
}
|
||||
@ -222,14 +222,14 @@ impl<'a> StyleSheetParser<'a> {
|
||||
&Scope::new(),
|
||||
&Selector::new(),
|
||||
None,
|
||||
span
|
||||
span,
|
||||
)?),
|
||||
AtRuleKind::Import => {
|
||||
devour_whitespace(self.lexer);
|
||||
let mut file_name = String::new();
|
||||
let next = match self.lexer.next() {
|
||||
Some(v) => v,
|
||||
None => todo!("expected input after @import")
|
||||
None => todo!("expected input after @import"),
|
||||
};
|
||||
match next.kind {
|
||||
q @ '"' | q @ '\'' => {
|
||||
@ -238,8 +238,12 @@ impl<'a> StyleSheetParser<'a> {
|
||||
self.lexer,
|
||||
&Scope::new(),
|
||||
q,
|
||||
&Selector::new())?
|
||||
.node.unquote().to_css_string(span)?);
|
||||
&Selector::new(),
|
||||
)?
|
||||
.node
|
||||
.unquote()
|
||||
.to_css_string(span)?,
|
||||
);
|
||||
}
|
||||
_ => return Err(("Expected string.", next.pos()).into()),
|
||||
}
|
||||
@ -251,14 +255,22 @@ impl<'a> StyleSheetParser<'a> {
|
||||
|
||||
devour_whitespace(self.lexer);
|
||||
|
||||
let (new_rules, new_scope) = import(self.path, file_name.as_ref(), &mut self.map)?;
|
||||
let (new_rules, new_scope) =
|
||||
import(self.path, file_name.as_ref(), &mut self.map)?;
|
||||
rules.extend(new_rules);
|
||||
GLOBAL_SCOPE.with(|s| {
|
||||
s.borrow_mut().extend(new_scope);
|
||||
});
|
||||
}
|
||||
v => {
|
||||
let rule = AtRule::from_tokens(v, span, self.lexer, &mut Scope::new(), &Selector::new(), None)?;
|
||||
let rule = AtRule::from_tokens(
|
||||
v,
|
||||
span,
|
||||
self.lexer,
|
||||
&mut Scope::new(),
|
||||
&Selector::new(),
|
||||
None,
|
||||
)?;
|
||||
match rule.node {
|
||||
AtRule::Mixin(name, mixin) => {
|
||||
insert_global_mixin(&name, *mixin);
|
||||
@ -274,17 +286,36 @@ impl<'a> StyleSheetParser<'a> {
|
||||
("This at-rule is not allowed here.", rule.span).into()
|
||||
)
|
||||
}
|
||||
AtRule::For(f) => rules.extend(f.ruleset_eval(&mut Scope::new(), &Selector::new(), None)?),
|
||||
AtRule::While(w) => rules.extend(w.ruleset_eval(&mut Scope::new(), &Selector::new(), true, None)?),
|
||||
AtRule::Each(e) => {
|
||||
rules.extend(e.ruleset_eval(&mut Scope::new(), &Selector::new(), None)?)
|
||||
}
|
||||
AtRule::For(f) => rules.extend(f.ruleset_eval(
|
||||
&mut Scope::new(),
|
||||
&Selector::new(),
|
||||
None,
|
||||
)?),
|
||||
AtRule::While(w) => rules.extend(w.ruleset_eval(
|
||||
&mut Scope::new(),
|
||||
&Selector::new(),
|
||||
true,
|
||||
None,
|
||||
)?),
|
||||
AtRule::Each(e) => rules.extend(e.ruleset_eval(
|
||||
&mut Scope::new(),
|
||||
&Selector::new(),
|
||||
None,
|
||||
)?),
|
||||
AtRule::Include(s) => rules.extend(s),
|
||||
AtRule::Content => return Err(
|
||||
("@content is only allowed within mixin declarations.", rule.span
|
||||
).into()),
|
||||
AtRule::Content => {
|
||||
return Err((
|
||||
"@content is only allowed within mixin declarations.",
|
||||
rule.span,
|
||||
)
|
||||
.into())
|
||||
}
|
||||
AtRule::If(i) => {
|
||||
rules.extend(i.eval(&mut Scope::new(), &Selector::new(), None)?);
|
||||
rules.extend(i.eval(
|
||||
&mut Scope::new(),
|
||||
&Selector::new(),
|
||||
None,
|
||||
)?);
|
||||
}
|
||||
AtRule::AtRoot(root_rules) => rules.extend(root_rules),
|
||||
AtRule::Unknown(..) => rules.push(rule.map_node(Stmt::AtRule)),
|
||||
@ -292,11 +323,13 @@ impl<'a> StyleSheetParser<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
'&' => {
|
||||
return Err(
|
||||
("Top-level selectors may not contain the parent selector \"&\".", self.lexer.next().unwrap().pos()).into(),
|
||||
return Err((
|
||||
"Top-level selectors may not contain the parent selector \"&\".",
|
||||
self.lexer.next().unwrap().pos(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
c if c.is_control() => {
|
||||
return Err(("expected selector.", self.lexer.next().unwrap().pos()).into());
|
||||
|
Loading…
x
Reference in New Issue
Block a user