create structure for identifiers

it looks like rustfmt also updated during this commit, so there are some
formatting changes
This commit is contained in:
ConnorSkees 2020-05-22 18:21:18 -04:00
parent 283a2097ff
commit 2102781f62
4 changed files with 150 additions and 85 deletions

View File

@ -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,

View File

@ -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)
}
}

View File

@ -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) {

View File

@ -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());