Treat - and _ as the same in identifiers

This commit is contained in:
ConnorSkees 2020-02-08 17:26:01 -05:00
parent 783e43b765
commit fa582b3316
8 changed files with 75 additions and 30 deletions

View File

@ -144,11 +144,11 @@ pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(
toks.next(); toks.next();
match name { match name {
Some(ref 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( None => args.insert(
format!("{}", args.len()), format!("{}", args.len()),
scope.vars.get(&v).unwrap().clone(), scope.get_var(&v).unwrap().clone(),
), ),
}; };
if let Some(ref mut s) = name { if let Some(ref mut s) = name {
@ -162,10 +162,10 @@ pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(
}) => { }) => {
toks.next(); toks.next();
match name { 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( None => args.insert(
format!("{}", args.len()), format!("{}", args.len()),
scope.vars.get(&v).unwrap().clone(), scope.get_var(&v).unwrap().clone(),
), ),
}; };
break; break;

View File

@ -342,9 +342,9 @@ impl Display for Pos {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Scope { pub(crate) struct Scope {
pub vars: HashMap<String, Value>, vars: HashMap<String, Value>,
pub mixins: HashMap<String, Mixin>, mixins: HashMap<String, Mixin>,
pub functions: HashMap<String, Function>, functions: HashMap<String, Function>,
} }
impl Scope { 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<Value> {
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<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) -> 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<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.vars.extend(other.vars);
self.mixins.extend(other.mixins); self.mixins.extend(other.mixins);
self.functions.extend(other.functions); self.functions.extend(other.functions);

View File

@ -75,7 +75,7 @@ impl Function {
None => arg.default.clone().expect("missing variable!"), None => arg.default.clone().expect("missing variable!"),
}, },
}; };
self.scope.vars.insert(arg.name.clone(), val); self.scope.insert_var(&arg.name, val);
} }
self self
} }

View File

@ -34,7 +34,7 @@ pub(crate) fn import<P: AsRef<Path>>(path: P) -> SassResult<(Vec<Stmt>, Scope)>
let (rules2, scope2) = let (rules2, scope2) =
StyleSheet::export_from_path(name.to_str().expect("path should be UTF-8"))?; StyleSheet::export_from_path(name.to_str().expect("path should be UTF-8"))?;
rules.extend(rules2); rules.extend(rules2);
scope.merge(scope2); scope.extend(scope2);
} }
} }
Ok((rules, scope)) Ok((rules, scope))

View File

@ -353,8 +353,8 @@ impl<'a> StyleSheetParser<'a> {
} }
let VariableDecl { val, default } = eat_variable_value(&mut self.lexer, &self.global_scope) let VariableDecl { val, default } = eat_variable_value(&mut self.lexer, &self.global_scope)
.unwrap_or_else(|err| self.error(err.0, &err.1)); .unwrap_or_else(|err| self.error(err.0, &err.1));
if !default || self.global_scope.vars.get(&name).is_none() { if !default || self.global_scope.get_var(&name).is_err() {
self.global_scope.vars.insert(name, val); self.global_scope.insert_var(&name, val);
} }
} }
TokenKind::MultilineComment(_) => { TokenKind::MultilineComment(_) => {
@ -413,7 +413,7 @@ 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.merge(new_scope); self.global_scope.extend(new_scope);
} }
TokenKind::AtRule(_) => { TokenKind::AtRule(_) => {
if let Some(Token { if let Some(Token {
@ -423,10 +423,10 @@ impl<'a> StyleSheetParser<'a> {
{ {
match AtRule::from_tokens(rule, pos, &mut self.lexer, &self.global_scope) { match AtRule::from_tokens(rule, pos, &mut self.lexer, &self.global_scope) {
AtRule::Mixin(name, mixin) => { AtRule::Mixin(name, mixin) => {
self.global_scope.mixins.insert(name, *mixin); self.global_scope.insert_mixin(&name, *mixin);
} }
AtRule::Function(name, func) => { 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::Charset(toks) => rules.push(Stmt::AtRule(AtRule::Charset(toks))),
AtRule::Error(pos, message) => self.error(pos, &message), AtRule::Error(pos, message) => self.error(pos, &message),
@ -458,10 +458,10 @@ impl<'a> StyleSheetParser<'a> {
#[allow(clippy::redundant_closure)] #[allow(clippy::redundant_closure)]
Expr::Styles(s) => stmts.extend(s.into_iter().map(Stmt::Style)), Expr::Styles(s) => stmts.extend(s.into_iter().map(Stmt::Style)),
Expr::MixinDecl(name, mixin) => { Expr::MixinDecl(name, mixin) => {
scope.mixins.insert(name, *mixin); scope.insert_mixin(&name, *mixin);
} }
Expr::FunctionDecl(name, func) => { Expr::FunctionDecl(name, func) => {
scope.functions.insert(name, *func); scope.insert_fn(&name, *func);
} }
Expr::Selector(s) => { Expr::Selector(s) => {
self.scope += 1; self.scope += 1;
@ -478,10 +478,10 @@ impl<'a> StyleSheetParser<'a> {
} }
Expr::VariableDecl(name, val) => { Expr::VariableDecl(name, val) => {
if self.scope == 0 { if self.scope == 0 {
scope.vars.insert(name.clone(), val.clone()); scope.insert_var(&name, val.clone());
self.global_scope.vars.insert(name, val); self.global_scope.insert_var(&name, val);
} else { } else {
scope.vars.insert(name, val); scope.insert_var(&name, val);
} }
} }
Expr::Include(rules) => stmts.extend(rules), Expr::Include(rules) => stmts.extend(rules),
@ -564,7 +564,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
toks.next(); toks.next();
devour_whitespace(toks); devour_whitespace(toks);
let VariableDecl { val, default } = eat_variable_value(toks, scope)?; 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))); return Ok(Some(Expr::VariableDecl(name, val)));
} }
} else { } else {

View File

@ -82,7 +82,7 @@ impl Mixin {
None => arg.default.clone().expect("missing variable!"), None => arg.default.clone().expect("missing variable!"),
}, },
}; };
self.scope.vars.insert(arg.name.clone(), val); self.scope.insert_var(&arg.name, val);
} }
self self
} }
@ -110,7 +110,7 @@ impl Mixin {
})); }));
} }
Expr::VariableDecl(name, val) => { Expr::VariableDecl(name, val) => {
self.scope.vars.insert(name, val); self.scope.insert_var(&name, val);
} }
Expr::MultilineComment(s) => stmts.push(Stmt::MultilineComment(s)), Expr::MultilineComment(s) => stmts.push(Stmt::MultilineComment(s)),
} }
@ -155,8 +155,8 @@ pub(crate) fn eat_include<I: Iterator<Item = Token>>(
devour_whitespace(toks); devour_whitespace(toks);
let mixin = match scope.mixins.get(&name) { let mixin = match scope.get_mixin(&name) {
Some(m) => m.clone(), Ok(m) => m.clone(),
_ => return Err((pos, String::from("Expected identifier."))), _ => return Err((pos, String::from("Expected identifier."))),
}; };

View File

@ -52,7 +52,7 @@ pub(crate) fn parse_interpolation<I: Iterator<Item = Token>>(
todo!("invalid character in interpolation") todo!("invalid character in interpolation")
} }
TokenKind::Variable(ref v) => val.extend( TokenKind::Variable(ref v) => val.extend(
Lexer::new(&scope.vars.get(v).unwrap().to_string()).collect::<Vec<Token>>(), Lexer::new(&scope.get_var(v).unwrap().to_string()).collect::<Vec<Token>>(),
), ),
TokenKind::Interpolation => val.extend(parse_interpolation(tokens, scope)), TokenKind::Interpolation => val.extend(parse_interpolation(tokens, scope)),
_ => val.push(tok), _ => val.push(tok),

View File

@ -197,9 +197,9 @@ impl Value {
}) => { }) => {
toks.next(); toks.next();
let args = eat_call_args(toks, scope); let args = eat_call_args(toks, scope);
let func = match scope.functions.get(&s) { let func = match scope.get_fn(&s) {
Some(f) => f, Ok(f) => f,
None => match GLOBAL_FUNCTIONS.get(&s) { Err(_) => match GLOBAL_FUNCTIONS.get(&s) {
Some(f) => return f(&args), Some(f) => return f(&args),
None => todo!("called undefined function"), None => todo!("called undefined function"),
}, },
@ -243,7 +243,7 @@ impl Value {
Some(Value::Ident(s, QuoteKind::Single)) Some(Value::Ident(s, QuoteKind::Single))
} }
TokenKind::Variable(ref v) => { TokenKind::Variable(ref v) => {
Some(scope.vars.get(v).expect("expected variable").clone()) Some(scope.get_var(v).expect("expected variable").clone())
} }
TokenKind::Interpolation => { TokenKind::Interpolation => {
let mut s = parse_interpolation(toks, scope) let mut s = parse_interpolation(toks, scope)