diff --git a/src/args.rs b/src/args.rs index a6824da..2f8eef8 100644 --- a/src/args.rs +++ b/src/args.rs @@ -144,11 +144,11 @@ pub(crate) fn eat_call_args>( toks.next(); match name { Some(ref name) => { - args.insert(name.clone(), scope.vars.get(&v).unwrap().clone()) + args.insert(name.clone(), scope.get_var(&v).unwrap().clone()) } None => args.insert( format!("{}", args.len()), - scope.vars.get(&v).unwrap().clone(), + scope.get_var(&v).unwrap().clone(), ), }; if let Some(ref mut s) = name { @@ -162,10 +162,10 @@ pub(crate) fn eat_call_args>( }) => { toks.next(); match name { - Some(name) => args.insert(name, scope.vars.get(&v).unwrap().clone()), + Some(name) => args.insert(name, scope.get_var(&v).unwrap().clone()), None => args.insert( format!("{}", args.len()), - scope.vars.get(&v).unwrap().clone(), + scope.get_var(&v).unwrap().clone(), ), }; break; diff --git a/src/common.rs b/src/common.rs index a9a86cb..2a77f0e 100644 --- a/src/common.rs +++ b/src/common.rs @@ -342,9 +342,9 @@ impl Display for Pos { #[derive(Debug, Clone)] pub(crate) struct Scope { - pub vars: HashMap, - pub mixins: HashMap, - pub functions: HashMap, + vars: HashMap, + mixins: HashMap, + functions: HashMap, } impl Scope { @@ -357,7 +357,52 @@ impl Scope { } } - pub fn merge(&mut self, other: Scope) { + pub fn get_var(&self, v: &str) -> Result<&Value, String> { + match self.vars.get(&v.replace('_', "-")) { + Some(v) => Ok(v), + None => Err(format!("Undefined variable `{}`.", v)) + } + } + + pub fn insert_var(&mut self, s: &str, v: Value) -> Option { + self.vars.insert(s.replace('_', "-"), v) + } + + pub fn var_exists(&self, v: &str) -> bool { + self.vars.contains_key(&v.replace('_', "-")) + } + + pub fn get_mixin(&self, v: &str) -> Result<&Mixin, String> { + match self.mixins.get(&v.replace('_', "-")) { + Some(v) => Ok(v), + None => Err(format!("Undefined mixin `{}`.", v)) + } + } + + pub fn insert_mixin(&mut self, s: &str, v: Mixin) -> Option { + 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) -> Result<&Function, String> { + match self.functions.get(&v.replace('_', "-")) { + Some(v) => Ok(v), + None => Err(format!("Undefined function `{}`.", v)) + } + } + + pub fn insert_fn(&mut self, s: &str, v: Function) -> Option { + 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); diff --git a/src/function.rs b/src/function.rs index d955186..1971559 100644 --- a/src/function.rs +++ b/src/function.rs @@ -75,7 +75,7 @@ impl Function { None => arg.default.clone().expect("missing variable!"), }, }; - self.scope.vars.insert(arg.name.clone(), val); + self.scope.insert_var(&arg.name, val); } self } diff --git a/src/imports.rs b/src/imports.rs index 0f2c13c..9db5997 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -34,7 +34,7 @@ pub(crate) fn import>(path: P) -> SassResult<(Vec, Scope)> let (rules2, scope2) = StyleSheet::export_from_path(name.to_str().expect("path should be UTF-8"))?; rules.extend(rules2); - scope.merge(scope2); + scope.extend(scope2); } } Ok((rules, scope)) diff --git a/src/lib.rs b/src/lib.rs index 5bde040..9528429 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -353,8 +353,8 @@ impl<'a> StyleSheetParser<'a> { } let VariableDecl { val, default } = eat_variable_value(&mut self.lexer, &self.global_scope) .unwrap_or_else(|err| self.error(err.0, &err.1)); - if !default || self.global_scope.vars.get(&name).is_none() { - self.global_scope.vars.insert(name, val); + if !default || self.global_scope.get_var(&name).is_err() { + self.global_scope.insert_var(&name, val); } } TokenKind::MultilineComment(_) => { @@ -413,7 +413,7 @@ impl<'a> StyleSheetParser<'a> { let (new_rules, new_scope) = import(file_name)?; rules.extend(new_rules); - self.global_scope.merge(new_scope); + self.global_scope.extend(new_scope); } TokenKind::AtRule(_) => { if let Some(Token { @@ -423,10 +423,10 @@ impl<'a> StyleSheetParser<'a> { { match AtRule::from_tokens(rule, pos, &mut self.lexer, &self.global_scope) { AtRule::Mixin(name, mixin) => { - self.global_scope.mixins.insert(name, *mixin); + self.global_scope.insert_mixin(&name, *mixin); } AtRule::Function(name, func) => { - self.global_scope.functions.insert(name, *func); + self.global_scope.insert_fn(&name, *func); } AtRule::Charset(toks) => rules.push(Stmt::AtRule(AtRule::Charset(toks))), AtRule::Error(pos, message) => self.error(pos, &message), @@ -458,10 +458,10 @@ impl<'a> StyleSheetParser<'a> { #[allow(clippy::redundant_closure)] Expr::Styles(s) => stmts.extend(s.into_iter().map(Stmt::Style)), Expr::MixinDecl(name, mixin) => { - scope.mixins.insert(name, *mixin); + scope.insert_mixin(&name, *mixin); } Expr::FunctionDecl(name, func) => { - scope.functions.insert(name, *func); + scope.insert_fn(&name, *func); } Expr::Selector(s) => { self.scope += 1; @@ -478,10 +478,10 @@ impl<'a> StyleSheetParser<'a> { } Expr::VariableDecl(name, val) => { if self.scope == 0 { - scope.vars.insert(name.clone(), val.clone()); - self.global_scope.vars.insert(name, val); + scope.insert_var(&name, val.clone()); + self.global_scope.insert_var(&name, val); } else { - scope.vars.insert(name, val); + scope.insert_var(&name, val); } } Expr::Include(rules) => stmts.extend(rules), @@ -564,7 +564,7 @@ pub(crate) fn eat_expr>( toks.next(); devour_whitespace(toks); let VariableDecl { val, default } = eat_variable_value(toks, scope)?; - if !default || scope.vars.get(&name).is_none() { + if !default || scope.get_var(&name).is_err() { return Ok(Some(Expr::VariableDecl(name, val))); } } else { diff --git a/src/mixin.rs b/src/mixin.rs index 76e099a..0c95d4d 100644 --- a/src/mixin.rs +++ b/src/mixin.rs @@ -82,7 +82,7 @@ impl Mixin { None => arg.default.clone().expect("missing variable!"), }, }; - self.scope.vars.insert(arg.name.clone(), val); + self.scope.insert_var(&arg.name, val); } self } @@ -110,7 +110,7 @@ impl Mixin { })); } Expr::VariableDecl(name, val) => { - self.scope.vars.insert(name, val); + self.scope.insert_var(&name, val); } Expr::MultilineComment(s) => stmts.push(Stmt::MultilineComment(s)), } @@ -155,8 +155,8 @@ pub(crate) fn eat_include>( devour_whitespace(toks); - let mixin = match scope.mixins.get(&name) { - Some(m) => m.clone(), + let mixin = match scope.get_mixin(&name) { + Ok(m) => m.clone(), _ => return Err((pos, String::from("Expected identifier."))), }; diff --git a/src/utils.rs b/src/utils.rs index 9d68986..d2c6783 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -52,7 +52,7 @@ pub(crate) fn parse_interpolation>( todo!("invalid character in interpolation") } TokenKind::Variable(ref v) => val.extend( - Lexer::new(&scope.vars.get(v).unwrap().to_string()).collect::>(), + Lexer::new(&scope.get_var(v).unwrap().to_string()).collect::>(), ), TokenKind::Interpolation => val.extend(parse_interpolation(tokens, scope)), _ => val.push(tok), diff --git a/src/value/parse.rs b/src/value/parse.rs index e447214..eed8b23 100644 --- a/src/value/parse.rs +++ b/src/value/parse.rs @@ -197,9 +197,9 @@ impl Value { }) => { toks.next(); let args = eat_call_args(toks, scope); - let func = match scope.functions.get(&s) { - Some(f) => f, - None => match GLOBAL_FUNCTIONS.get(&s) { + let func = match scope.get_fn(&s) { + Ok(f) => f, + Err(_) => match GLOBAL_FUNCTIONS.get(&s) { Some(f) => return f(&args), None => todo!("called undefined function"), }, @@ -243,7 +243,7 @@ impl Value { Some(Value::Ident(s, QuoteKind::Single)) } TokenKind::Variable(ref v) => { - Some(scope.vars.get(v).expect("expected variable").clone()) + Some(scope.get_var(v).expect("expected variable").clone()) } TokenKind::Interpolation => { let mut s = parse_interpolation(toks, scope)