From 9a6c8ce0195371795aecb62c3261f52682001242 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Thu, 21 May 2020 22:45:31 -0400 Subject: [PATCH] add string interning this seems to improve performance by ~20% as well as significantly decrease memory usage --- Cargo.toml | 1 + src/atrule/each_rule.rs | 4 +- src/builtin/color/hsl.rs | 35 +- src/builtin/color/opacity.rs | 3 +- src/builtin/color/rgb.rs | 42 ++- src/builtin/list.rs | 18 +- src/builtin/meta.rs | 34 +- src/builtin/string.rs | 49 ++- src/color/mod.rs | 7 +- src/color/name.rs | 714 ++++++++++++++++++++++++++++------- src/common.rs | 36 ++ src/interner.rs | 55 +++ src/lib.rs | 10 +- src/scope.rs | 114 +++--- src/selector/attribute.rs | 12 +- src/selector/mod.rs | 8 +- src/style.rs | 27 +- src/stylesheet.rs | 100 +++-- src/utils/strings.rs | 7 +- src/value/css_function.rs | 2 +- src/value/mod.rs | 9 +- src/value/ops.rs | 199 +++++++--- src/value/parse.rs | 147 +++++--- src/value/sass_function.rs | 7 +- 24 files changed, 1195 insertions(+), 445 deletions(-) create mode 100644 src/interner.rs diff --git a/Cargo.toml b/Cargo.toml index 65dea74..0cccbf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ rand = { version = "0.7.3", optional = true } codemap = "0.1.3" peekmore = "0.4.0" wasm-bindgen = { version = "0.2.60", optional = true } +lasso = "0.2.2" [features] default = ["commandline", "random"] diff --git a/src/atrule/each_rule.rs b/src/atrule/each_rule.rs index 6b4f3ef..676d908 100644 --- a/src/atrule/each_rule.rs +++ b/src/atrule/each_rule.rs @@ -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, diff --git a/src/builtin/color/hsl.rs b/src/builtin/color/hsl.rs index dca421a..123c1c6 100644 --- a/src/builtin/color/hsl.rs +++ b/src/builtin/color/hsl.rs @@ -6,6 +6,7 @@ use crate::args::CallArgs; use crate::color::Color; use crate::common::QuoteKind; use crate::error::SassResult; +use crate::interner::InternedString; use crate::scope::Scope; use crate::selector::Selector; use crate::unit::Unit; @@ -107,7 +108,10 @@ fn inner_hsl( ); } string.push(')'); - return Ok(Value::Ident(string, QuoteKind::None)); + return Ok(Value::Ident( + InternedString::get_or_intern(string), + QuoteKind::None, + )); } v => { return Err(( @@ -136,7 +140,10 @@ fn inner_hsl( ); } string.push(')'); - return Ok(Value::Ident(string, QuoteKind::None)); + return Ok(Value::Ident( + InternedString::get_or_intern(string), + QuoteKind::None, + )); } v => { return Err(( @@ -167,7 +174,10 @@ fn inner_hsl( ); } string.push(')'); - return Ok(Value::Ident(string, QuoteKind::None)); + return Ok(Value::Ident( + InternedString::get_or_intern(string), + QuoteKind::None, + )); } v => { return Err(( @@ -201,14 +211,14 @@ fn inner_hsl( } v if v.is_special_function() => { return Ok(Value::Ident( - format!( + InternedString::get_or_intern(format!( "{}({}, {}, {}, {})", name, hue, saturation, lightness, v.to_css_string(args.span())? - ), + )), QuoteKind::None, )); } @@ -358,10 +368,10 @@ fn saturate(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sas args.max_args(2)?; if args.len() == 1 { return Ok(Value::Ident( - format!( + InternedString::get_or_intern(format!( "saturate({})", arg!(args, scope, super_selector, 0, "amount").to_css_string(args.span())? - ), + )), QuoteKind::None, )); } @@ -383,7 +393,7 @@ fn saturate(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sas Value::Color(c) => c, Value::Dimension(n, u) => { return Ok(Value::Ident( - format!("saturate({}{})", n, u), + InternedString::get_or_intern(format!("saturate({}{})", n, u)), QuoteKind::None, )) } @@ -432,7 +442,7 @@ fn grayscale(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa Value::Color(c) => c, Value::Dimension(n, u) => { return Ok(Value::Ident( - format!("grayscale({}{})", n, u), + InternedString::get_or_intern(format!("grayscale({}{})", n, u)), QuoteKind::None, )) } @@ -485,9 +495,10 @@ fn invert(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassR }; match arg!(args, scope, super_selector, 0, "color") { Value::Color(c) => Ok(Value::Color(Box::new(c.invert(weight)))), - Value::Dimension(n, Unit::Percent) => { - Ok(Value::Ident(format!("invert({}%)", n), QuoteKind::None)) - } + Value::Dimension(n, Unit::Percent) => Ok(Value::Ident( + InternedString::get_or_intern(format!("invert({}%)", n)), + QuoteKind::None, + )), Value::Dimension(..) => Err(( "Only one argument may be passed to the plain-CSS invert() function.", args.span(), diff --git a/src/builtin/color/opacity.rs b/src/builtin/color/opacity.rs index 1b2e25d..31d138f 100644 --- a/src/builtin/color/opacity.rs +++ b/src/builtin/color/opacity.rs @@ -3,6 +3,7 @@ use super::{Builtin, GlobalFunctionMap}; use crate::args::CallArgs; use crate::common::QuoteKind; use crate::error::SassResult; +use crate::interner::InternedString; use crate::scope::Scope; use crate::selector::Selector; use crate::unit::Unit; @@ -26,7 +27,7 @@ fn opacity(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sass match arg!(args, scope, super_selector, 0, "color") { Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None)), Value::Dimension(num, unit) => Ok(Value::Ident( - format!("opacity({}{})", num, unit), + InternedString::get_or_intern(format!("opacity({}{})", num, unit)), QuoteKind::None, )), v => Err(( diff --git a/src/builtin/color/rgb.rs b/src/builtin/color/rgb.rs index dbb9a3a..4663312 100644 --- a/src/builtin/color/rgb.rs +++ b/src/builtin/color/rgb.rs @@ -6,6 +6,7 @@ use crate::args::CallArgs; use crate::color::Color; use crate::common::QuoteKind; use crate::error::SassResult; +use crate::interner::InternedString; use crate::scope::Scope; use crate::selector::Selector; use crate::unit::Unit; @@ -46,13 +47,13 @@ fn inner_rgb( let green = channels.pop().unwrap(); let red = channels.pop().unwrap(); return Ok(Value::Ident( - format!( + InternedString::get_or_intern(format!( "{}({}, {}, {})", name, red.to_css_string(args.span())?, green.to_css_string(args.span())?, v.to_css_string(args.span())? - ), + )), QuoteKind::None, )); } @@ -80,7 +81,10 @@ fn inner_rgb( ), None => format!("{}({} {})", name, v.to_css_string(args.span())?, blue), }; - return Ok(Value::Ident(string, QuoteKind::None)); + return Ok(Value::Ident( + InternedString::get_or_intern(string), + QuoteKind::None, + )); } Some(v) => { return Err(( @@ -97,13 +101,13 @@ fn inner_rgb( Some(Value::Dimension(n, Unit::Percent)) => (n / Number::from(100)) * Number::from(255), Some(v) if v.is_special_function() => { return Ok(Value::Ident( - format!( + InternedString::get_or_intern(format!( "{}({}, {}, {})", name, v.to_css_string(args.span())?, green, blue - ), + )), QuoteKind::None, )); } @@ -126,12 +130,12 @@ fn inner_rgb( v if v.is_special_function() => { let alpha = arg!(args, scope, super_selector, 1, "alpha"); return Ok(Value::Ident( - format!( + InternedString::get_or_intern(format!( "{}({}, {})", name, v.to_css_string(args.span())?, alpha.to_css_string(args.span())? - ), + )), QuoteKind::None, )); } @@ -158,14 +162,14 @@ fn inner_rgb( } v if v.is_special_function() => { return Ok(Value::Ident( - format!( + InternedString::get_or_intern(format!( "{}({}, {}, {}, {})", name, color.red(), color.green(), color.blue(), v.to_css_string(args.span())? - ), + )), QuoteKind::None, )); } @@ -210,7 +214,10 @@ fn inner_rgb( ); } string.push(')'); - return Ok(Value::Ident(string, QuoteKind::None)); + return Ok(Value::Ident( + InternedString::get_or_intern(string), + QuoteKind::None, + )); } v => { return Err(( @@ -250,7 +257,10 @@ fn inner_rgb( ); } string.push(')'); - return Ok(Value::Ident(string, QuoteKind::None)); + return Ok(Value::Ident( + InternedString::get_or_intern(string), + QuoteKind::None, + )); } v => { return Err(( @@ -289,7 +299,10 @@ fn inner_rgb( ); } string.push(')'); - return Ok(Value::Ident(string, QuoteKind::None)); + return Ok(Value::Ident( + InternedString::get_or_intern(string), + QuoteKind::None, + )); } v => { return Err(( @@ -327,7 +340,10 @@ fn inner_rgb( blue, v.to_css_string(args.span())? ); - return Ok(Value::Ident(string, QuoteKind::None)); + return Ok(Value::Ident( + InternedString::get_or_intern(string), + QuoteKind::None, + )); } v => { return Err(( diff --git a/src/builtin/list.rs b/src/builtin/list.rs index 47a93b2..bcca5c8 100644 --- a/src/builtin/list.rs +++ b/src/builtin/list.rs @@ -5,6 +5,7 @@ use num_traits::{One, Signed, ToPrimitive, Zero}; use crate::args::CallArgs; use crate::common::{Brackets, ListSeparator, QuoteKind}; use crate::error::SassResult; +use crate::interner::InternedString; use crate::scope::Scope; use crate::selector::Selector; use crate::unit::Unit; @@ -72,11 +73,10 @@ fn list_separator( ) -> SassResult { args.max_args(1)?; Ok(Value::Ident( - match arg!(args, scope, super_selector, 0, "list") { + InternedString::get_or_intern(match arg!(args, scope, super_selector, 0, "list") { Value::List(_, sep, ..) => sep.name(), _ => ListSeparator::Space.name(), - } - .to_owned(), + }), QuoteKind::None, )) } @@ -140,9 +140,9 @@ fn append(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassR scope, super_selector, 2, - "separator" = Value::Ident("auto".to_owned(), QuoteKind::None) + "separator" = Value::Ident(InternedString::get_or_intern("auto"), QuoteKind::None) ) { - Value::Ident(s, ..) => match s.as_str() { + Value::Ident(s, ..) => match s.resolve().as_str() { "auto" => sep, "comma" => ListSeparator::Comma, "space" => ListSeparator::Space, @@ -188,9 +188,9 @@ fn join(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRes scope, super_selector, 2, - "separator" = Value::Ident("auto".to_owned(), QuoteKind::None) + "separator" = Value::Ident(InternedString::get_or_intern("auto"), QuoteKind::None) ) { - Value::Ident(s, ..) => match s.as_str() { + Value::Ident(s, ..) => match s.resolve().as_str() { "auto" => { if list1.is_empty() || (list1.len() == 1 && sep1 == ListSeparator::Space) { sep2 @@ -225,9 +225,9 @@ fn join(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRes scope, super_selector, 3, - "bracketed" = Value::Ident("auto".to_owned(), QuoteKind::None) + "bracketed" = Value::Ident(InternedString::get_or_intern("auto"), QuoteKind::None) ) { - Value::Ident(s, ..) => match s.as_str() { + Value::Ident(s, ..) => match s.resolve().as_str() { "auto" => brackets, _ => Brackets::Bracketed, }, diff --git a/src/builtin/meta.rs b/src/builtin/meta.rs index 02b0b43..29c9969 100644 --- a/src/builtin/meta.rs +++ b/src/builtin/meta.rs @@ -5,6 +5,7 @@ use codemap::Spanned; use crate::args::CallArgs; use crate::common::QuoteKind; use crate::error::SassResult; +use crate::interner::InternedString; use crate::scope::global_var_exists; use crate::scope::Scope; use crate::selector::Selector; @@ -27,7 +28,7 @@ fn feature_exists( ) -> SassResult { args.max_args(1)?; match arg!(args, scope, super_selector, 0, "feature") { - Value::Ident(s, _) => Ok(match s.as_str() { + Value::Ident(s, _) => Ok(match s.resolve().as_str() { // A local variable will shadow a global variable unless // `!global` is used. "global-variable-shadowing" => Value::True, @@ -71,14 +72,17 @@ fn unit(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRes .into()) } }; - Ok(Value::Ident(unit, QuoteKind::Quoted)) + Ok(Value::Ident( + InternedString::get_or_intern(unit), + QuoteKind::Quoted, + )) } fn type_of(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { args.max_args(1)?; let value = arg!(args, scope, super_selector, 0, "value"); Ok(Value::Ident( - value.kind(args.span())?.to_owned(), + InternedString::get_or_intern(value.kind(args.span())?), QuoteKind::None, )) } @@ -95,9 +99,9 @@ fn unitless(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sas fn inspect(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { args.max_args(1)?; Ok(Value::Ident( - arg!(args, scope, super_selector, 0, "value") - .inspect(args.span())? - .into(), + InternedString::get_or_intern( + arg!(args, scope, super_selector, 0, "value").inspect(args.span())?, + ), QuoteKind::None, )) } @@ -109,7 +113,7 @@ fn variable_exists( ) -> SassResult { args.max_args(1)?; match arg!(args, scope, super_selector, 0, "name") { - Value::Ident(s, _) => Ok(Value::bool(scope.var_exists(&s))), + Value::Ident(s, _) => Ok(Value::bool(scope.var_exists(s))), v => Err(( format!("$name: {} is not a string.", v.to_css_string(args.span())?), args.span(), @@ -125,7 +129,7 @@ fn global_variable_exists( ) -> SassResult { args.max_args(1)?; match arg!(args, scope, super_selector, 0, "name") { - Value::Ident(s, _) => Ok(Value::bool(global_var_exists(&s))), + Value::Ident(s, _) => Ok(Value::bool(global_var_exists(s))), v => Err(( format!("$name: {} is not a string.", v.to_css_string(args.span())?), args.span(), @@ -137,7 +141,7 @@ fn global_variable_exists( fn mixin_exists(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { args.max_args(2)?; match arg!(args, scope, super_selector, 0, "name") { - Value::Ident(s, _) => Ok(Value::bool(scope.mixin_exists(&s))), + Value::Ident(s, _) => Ok(Value::bool(scope.mixin_exists(s))), v => Err(( format!("$name: {} is not a string.", v.to_css_string(args.span())?), args.span(), @@ -153,9 +157,7 @@ fn function_exists( ) -> SassResult { args.max_args(2)?; match arg!(args, scope, super_selector, 0, "name") { - Value::Ident(s, _) => Ok(Value::bool( - scope.fn_exists(&s) || GLOBAL_FUNCTIONS.contains_key(s.as_str()), - )), + Value::Ident(s, _) => Ok(Value::bool(scope.fn_exists(s))), v => Err(( format!("$name: {} is not a string.", v.to_css_string(args.span())?), args.span(), @@ -201,12 +203,12 @@ fn get_function(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> } let func = match scope.get_fn(Spanned { - node: name.clone(), + node: name, span: args.span(), }) { - Ok(f) => SassFunction::UserDefined(Box::new(f), name), - Err(..) => match GLOBAL_FUNCTIONS.get(name.as_str()) { - Some(f) => SassFunction::Builtin(f.clone(), name), + Ok(f) => SassFunction::UserDefined(Box::new(f), name.into()), + Err(..) => match GLOBAL_FUNCTIONS.get(&name.resolve().as_str()) { + Some(f) => SassFunction::Builtin(f.clone(), name.into()), None => return Err((format!("Function not found: {}", name), args.span()).into()), }, }; diff --git a/src/builtin/string.rs b/src/builtin/string.rs index 3853e32..6be4b14 100644 --- a/src/builtin/string.rs +++ b/src/builtin/string.rs @@ -9,6 +9,7 @@ use rand::{distributions::Alphanumeric, thread_rng, Rng}; use crate::args::CallArgs; use crate::common::QuoteKind; use crate::error::SassResult; +use crate::interner::{keywords::EMPTY_STRING, InternedString}; use crate::scope::Scope; use crate::selector::Selector; use crate::unit::Unit; @@ -21,9 +22,10 @@ fn to_upper_case( ) -> SassResult { args.max_args(1)?; match arg!(args, scope, super_selector, 0, "string") { - Value::Ident(mut i, q) => { + Value::Ident(i, q) => { + let mut i = i.resolve(); i.make_ascii_uppercase(); - Ok(Value::Ident(i, q)) + Ok(Value::Ident(InternedString::get_or_intern(i), q)) } v => Err(( format!( @@ -43,9 +45,10 @@ fn to_lower_case( ) -> SassResult { args.max_args(1)?; match arg!(args, scope, super_selector, 0, "string") { - Value::Ident(mut i, q) => { + Value::Ident(i, q) => { + let mut i = i.resolve(); i.make_ascii_lowercase(); - Ok(Value::Ident(i, q)) + Ok(Value::Ident(InternedString::get_or_intern(i), q)) } v => Err(( format!( @@ -62,7 +65,7 @@ fn str_length(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S args.max_args(1)?; match arg!(args, scope, super_selector, 0, "string") { Value::Ident(i, _) => Ok(Value::Dimension( - Number::from(i.chars().count()), + Number::from(i.resolve().chars().count()), Unit::None, )), v => Err(( @@ -121,7 +124,7 @@ fn str_slice(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa .into()) } }; - let str_len = string.chars().count(); + let str_len = string.resolve().chars().count(); let start = match arg!(args, scope, super_selector, 1, "start-at") { Value::Dimension(n, Unit::None) if n.is_decimal() => { return Err((format!("{} is not an int.", n), args.span()).into()) @@ -195,14 +198,17 @@ fn str_slice(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa } if start > end || start > str_len { - Ok(Value::Ident(String::new(), quotes)) + Ok(Value::Ident(*EMPTY_STRING, quotes)) } else { Ok(Value::Ident( - string - .chars() - .skip(start - 1) - .take(end - start + 1) - .collect(), + InternedString::get_or_intern( + string + .resolve() + .chars() + .skip(start - 1) + .take(end - start + 1) + .collect::(), + ), quotes, )) } @@ -211,7 +217,7 @@ fn str_slice(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa fn str_index(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { args.max_args(2)?; let s1 = match arg!(args, scope, super_selector, 0, "string") { - Value::Ident(i, _) => i, + Value::Ident(i, _) => i.resolve(), v => { return Err(( format!( @@ -225,7 +231,7 @@ fn str_index(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa }; let substr = match arg!(args, scope, super_selector, 1, "substring") { - Value::Ident(i, _) => i, + Value::Ident(i, _) => i.resolve(), v => { return Err(( format!( @@ -247,7 +253,7 @@ fn str_index(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa fn str_insert(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { args.max_args(3)?; let (s1, quotes) = match arg!(args, scope, super_selector, 0, "string") { - Value::Ident(i, q) => (i, q), + Value::Ident(i, q) => (i.resolve().to_string(), q), v => { return Err(( format!( @@ -261,7 +267,7 @@ fn str_insert(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S }; let substr = match arg!(args, scope, super_selector, 1, "insert") { - Value::Ident(i, _) => i, + Value::Ident(i, _) => i.resolve().to_string(), v => { return Err(( format!( @@ -299,7 +305,7 @@ fn str_insert(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S }; if s1.is_empty() { - return Ok(Value::Ident(substr, quotes)); + return Ok(Value::Ident(InternedString::get_or_intern(substr), quotes)); } let len = s1.chars().count(); @@ -342,18 +348,21 @@ fn str_insert(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S } }; - Ok(Value::Ident(string, quotes)) + Ok(Value::Ident(InternedString::get_or_intern(string), quotes)) } #[cfg(feature = "random")] fn unique_id(args: CallArgs, _: &Scope, _: &Selector) -> SassResult { args.max_args(0)?; let mut rng = thread_rng(); - let string = std::iter::repeat(()) + let string: String = std::iter::repeat(()) .map(|()| rng.sample(Alphanumeric)) .take(7) .collect(); - Ok(Value::Ident(string, QuoteKind::None)) + Ok(Value::Ident( + InternedString::get_or_intern(string), + QuoteKind::None, + )) } pub(crate) fn declare(f: &mut GlobalFunctionMap) { diff --git a/src/color/mod.rs b/src/color/mod.rs index 7014159..6a8c4b7 100644 --- a/src/color/mod.rs +++ b/src/color/mod.rs @@ -17,6 +17,7 @@ use std::fmt::{self, Display}; +use crate::interner::InternedString; use crate::value::Number; pub(crate) use name::NAMED_COLORS; @@ -468,14 +469,14 @@ impl Color { /// Other color functions impl Color { - pub fn to_ie_hex_str(&self) -> String { - format!( + pub fn to_ie_hex_str(&self) -> InternedString { + InternedString::get_or_intern(format!( "#{:X}{:X}{:X}{:X}", (self.alpha() * Number::from(255)).round().to_integer(), self.red().to_integer(), self.green().to_integer(), self.blue().to_integer() - ) + )) } } diff --git a/src/color/name.rs b/src/color/name.rs index 2461c33..16030d8 100644 --- a/src/color/name.rs +++ b/src/color/name.rs @@ -4,9 +4,11 @@ use once_cell::sync::Lazy; use std::collections::HashMap; +use crate::interner::InternedString; + pub(crate) struct NamedColorMap { - name_to_rgba: HashMap<&'static str, [u8; 4]>, - rgba_to_name: HashMap<[u8; 4], &'static str>, + name_to_rgba: HashMap, + rgba_to_name: HashMap<[u8; 4], InternedString>, } impl NamedColorMap { @@ -17,161 +19,581 @@ impl NamedColorMap { } } - pub fn insert(&mut self, name: &'static str, rgba: [u8; 4]) { + fn insert(&mut self, name: InternedString, rgba: [u8; 4]) { self.name_to_rgba.insert(name, rgba); self.rgba_to_name.insert(rgba, name); } - pub fn get_by_name(&self, name: &str) -> Option<&[u8; 4]> { - self.name_to_rgba.get(name) + pub fn get_by_name(&self, name: InternedString) -> Option<&[u8; 4]> { + self.name_to_rgba.get(&name) } - pub fn get_by_rgba(&self, rgba: [u8; 4]) -> Option<&&str> { + pub fn get_by_rgba(&self, rgba: [u8; 4]) -> Option<&InternedString> { self.rgba_to_name.get(&rgba) } } pub(crate) static NAMED_COLORS: Lazy = Lazy::new(|| { let mut m = NamedColorMap::with_capacity(150); - m.insert("aliceblue", [0xF0, 0xF8, 0xFF, 0xFF]); - m.insert("antiquewhite", [0xFA, 0xEB, 0xD7, 0xFF]); - m.insert("aqua", [0x00, 0xFF, 0xFF, 0xFF]); - m.insert("aquamarine", [0x7F, 0xFF, 0xD4, 0xFF]); - m.insert("azure", [0xF0, 0xFF, 0xFF, 0xFF]); - m.insert("beige", [0xF5, 0xF5, 0xDC, 0xFF]); - m.insert("bisque", [0xFF, 0xE4, 0xC4, 0xFF]); - m.insert("black", [0x00, 0x00, 0x00, 0xFF]); - m.insert("blanchedalmond", [0xFF, 0xEB, 0xCD, 0xFF]); - m.insert("blue", [0x00, 0x00, 0xFF, 0xFF]); - m.insert("blueviolet", [0x8A, 0x2B, 0xE2, 0xFF]); - m.insert("brown", [0xA5, 0x2A, 0x2A, 0xFF]); - m.insert("burlywood", [0xDE, 0xB8, 0x87, 0xFF]); - m.insert("cadetblue", [0x5F, 0x9E, 0xA0, 0xFF]); - m.insert("chartreuse", [0x7F, 0xFF, 0x00, 0xFF]); - m.insert("chocolate", [0xD2, 0x69, 0x1E, 0xFF]); - m.insert("coral", [0xFF, 0x7F, 0x50, 0xFF]); - m.insert("cornflowerblue", [0x64, 0x95, 0xED, 0xFF]); - m.insert("cornsilk", [0xFF, 0xF8, 0xDC, 0xFF]); - m.insert("crimson", [0xDC, 0x14, 0x3C, 0xFF]); - m.insert("darkblue", [0x00, 0x00, 0x8B, 0xFF]); - m.insert("darkcyan", [0x00, 0x8B, 0x8B, 0xFF]); - m.insert("darkgoldenrod", [0xB8, 0x86, 0x0B, 0xFF]); - m.insert("darkgray", [0xA9, 0xA9, 0xA9, 0xFF]); - m.insert("darkgreen", [0x00, 0x64, 0x00, 0xFF]); - m.insert("darkkhaki", [0xBD, 0xB7, 0x6B, 0xFF]); - m.insert("darkmagenta", [0x8B, 0x00, 0x8B, 0xFF]); - m.insert("darkolivegreen", [0x55, 0x6B, 0x2F, 0xFF]); - m.insert("darkorange", [0xFF, 0x8C, 0x00, 0xFF]); - m.insert("darkorchid", [0x99, 0x32, 0xCC, 0xFF]); - m.insert("darkred", [0x8B, 0x00, 0x00, 0xFF]); - m.insert("darksalmon", [0xE9, 0x96, 0x7A, 0xFF]); - m.insert("darkseagreen", [0x8F, 0xBC, 0x8F, 0xFF]); - m.insert("darkslateblue", [0x48, 0x3D, 0x8B, 0xFF]); - m.insert("darkslategray", [0x2F, 0x4F, 0x4F, 0xFF]); - m.insert("darkturquoise", [0x00, 0xCE, 0xD1, 0xFF]); - m.insert("darkviolet", [0x94, 0x00, 0xD3, 0xFF]); - m.insert("deeppink", [0xFF, 0x14, 0x93, 0xFF]); - m.insert("deepskyblue", [0x00, 0xBF, 0xFF, 0xFF]); - m.insert("dimgray", [0x69, 0x69, 0x69, 0xFF]); - m.insert("dodgerblue", [0x1E, 0x90, 0xFF, 0xFF]); - m.insert("firebrick", [0xB2, 0x22, 0x22, 0xFF]); - m.insert("floralwhite", [0xFF, 0xFA, 0xF0, 0xFF]); - m.insert("forestgreen", [0x22, 0x8B, 0x22, 0xFF]); - m.insert("fuchsia", [0xFF, 0x00, 0xFF, 0xFF]); - m.insert("gainsboro", [0xDC, 0xDC, 0xDC, 0xFF]); - m.insert("ghostwhite", [0xF8, 0xF8, 0xFF, 0xFF]); - m.insert("gold", [0xFF, 0xD7, 0x00, 0xFF]); - m.insert("goldenrod", [0xDA, 0xA5, 0x20, 0xFF]); - m.insert("gray", [0x80, 0x80, 0x80, 0xFF]); - m.insert("green", [0x00, 0x80, 0x00, 0xFF]); - m.insert("greenyellow", [0xAD, 0xFF, 0x2F, 0xFF]); - m.insert("honeydew", [0xF0, 0xFF, 0xF0, 0xFF]); - m.insert("hotpink", [0xFF, 0x69, 0xB4, 0xFF]); - m.insert("indianred", [0xCD, 0x5C, 0x5C, 0xFF]); - m.insert("indigo", [0x4B, 0x00, 0x82, 0xFF]); - m.insert("ivory", [0xFF, 0xFF, 0xF0, 0xFF]); - m.insert("khaki", [0xF0, 0xE6, 0x8C, 0xFF]); - m.insert("lavender", [0xE6, 0xE6, 0xFA, 0xFF]); - m.insert("lavenderblush", [0xFF, 0xF0, 0xF5, 0xFF]); - m.insert("lawngreen", [0x7C, 0xFC, 0x00, 0xFF]); - m.insert("lemonchiffon", [0xFF, 0xFA, 0xCD, 0xFF]); - m.insert("lightblue", [0xAD, 0xD8, 0xE6, 0xFF]); - m.insert("lightcoral", [0xF0, 0x80, 0x80, 0xFF]); - m.insert("lightcyan", [0xE0, 0xFF, 0xFF, 0xFF]); - m.insert("lightgoldenrodyellow", [0xFA, 0xFA, 0xD2, 0xFF]); - m.insert("lightgray", [0xD3, 0xD3, 0xD3, 0xFF]); - m.insert("lightgreen", [0x90, 0xEE, 0x90, 0xFF]); - m.insert("lightpink", [0xFF, 0xB6, 0xC1, 0xFF]); - m.insert("lightsalmon", [0xFF, 0xA0, 0x7A, 0xFF]); - m.insert("lightseagreen", [0x20, 0xB2, 0xAA, 0xFF]); - m.insert("lightskyblue", [0x87, 0xCE, 0xFA, 0xFF]); - m.insert("lightslategray", [0x77, 0x88, 0x99, 0xFF]); - m.insert("lightsteelblue", [0xB0, 0xC4, 0xDE, 0xFF]); - m.insert("lightyellow", [0xFF, 0xFF, 0xE0, 0xFF]); - m.insert("lime", [0x00, 0xFF, 0x00, 0xFF]); - m.insert("limegreen", [0x32, 0xCD, 0x32, 0xFF]); - m.insert("linen", [0xFA, 0xF0, 0xE6, 0xFF]); - m.insert("maroon", [0x80, 0x00, 0x00, 0xFF]); - m.insert("mediumaquamarine", [0x66, 0xCD, 0xAA, 0xFF]); - m.insert("mediumblue", [0x00, 0x00, 0xCD, 0xFF]); - m.insert("mediumorchid", [0xBA, 0x55, 0xD3, 0xFF]); - m.insert("mediumpurple", [0x93, 0x70, 0xDB, 0xFF]); - m.insert("mediumseagreen", [0x3C, 0xB3, 0x71, 0xFF]); - m.insert("mediumslateblue", [0x7B, 0x68, 0xEE, 0xFF]); - m.insert("mediumspringgreen", [0x00, 0xFA, 0x9A, 0xFF]); - m.insert("mediumturquoise", [0x48, 0xD1, 0xCC, 0xFF]); - m.insert("mediumvioletred", [0xC7, 0x15, 0x85, 0xFF]); - m.insert("midnightblue", [0x19, 0x19, 0x70, 0xFF]); - m.insert("mintcream", [0xF5, 0xFF, 0xFA, 0xFF]); - m.insert("mistyrose", [0xFF, 0xE4, 0xE1, 0xFF]); - m.insert("moccasin", [0xFF, 0xE4, 0xB5, 0xFF]); - m.insert("navajowhite", [0xFF, 0xDE, 0xAD, 0xFF]); - m.insert("navy", [0x00, 0x00, 0x80, 0xFF]); - m.insert("oldlace", [0xFD, 0xF5, 0xE6, 0xFF]); - m.insert("olive", [0x80, 0x80, 0x00, 0xFF]); - m.insert("olivedrab", [0x6B, 0x8E, 0x23, 0xFF]); - m.insert("orange", [0xFF, 0xA5, 0x00, 0xFF]); - m.insert("orangered", [0xFF, 0x45, 0x00, 0xFF]); - m.insert("orchid", [0xDA, 0x70, 0xD6, 0xFF]); - m.insert("palegoldenrod", [0xEE, 0xE8, 0xAA, 0xFF]); - m.insert("palegreen", [0x98, 0xFB, 0x98, 0xFF]); - m.insert("paleturquoise", [0xAF, 0xEE, 0xEE, 0xFF]); - m.insert("palevioletred", [0xDB, 0x70, 0x93, 0xFF]); - m.insert("papayawhip", [0xFF, 0xEF, 0xD5, 0xFF]); - m.insert("peachpuff", [0xFF, 0xDA, 0xB9, 0xFF]); - m.insert("peru", [0xCD, 0x85, 0x3F, 0xFF]); - m.insert("pink", [0xFF, 0xC0, 0xCB, 0xFF]); - m.insert("plum", [0xDD, 0xA0, 0xDD, 0xFF]); - m.insert("powderblue", [0xB0, 0xE0, 0xE6, 0xFF]); - m.insert("purple", [0x80, 0x00, 0x80, 0xFF]); - m.insert("rebeccapurple", [0x66, 0x33, 0x99, 0xFF]); - m.insert("red", [0xFF, 0x00, 0x00, 0xFF]); - m.insert("rosybrown", [0xBC, 0x8F, 0x8F, 0xFF]); - m.insert("royalblue", [0x41, 0x69, 0xE1, 0xFF]); - m.insert("saddlebrown", [0x8B, 0x45, 0x13, 0xFF]); - m.insert("salmon", [0xFA, 0x80, 0x72, 0xFF]); - m.insert("sandybrown", [0xF4, 0xA4, 0x60, 0xFF]); - m.insert("seagreen", [0x2E, 0x8B, 0x57, 0xFF]); - m.insert("seashell", [0xFF, 0xF5, 0xEE, 0xFF]); - m.insert("sienna", [0xA0, 0x52, 0x2D, 0xFF]); - m.insert("silver", [0xC0, 0xC0, 0xC0, 0xFF]); - m.insert("skyblue", [0x87, 0xCE, 0xEB, 0xFF]); - m.insert("slateblue", [0x6A, 0x5A, 0xCD, 0xFF]); - m.insert("slategray", [0x70, 0x80, 0x90, 0xFF]); - m.insert("snow", [0xFF, 0xFA, 0xFA, 0xFF]); - m.insert("springgreen", [0x00, 0xFF, 0x7F, 0xFF]); - m.insert("steelblue", [0x46, 0x82, 0xB4, 0xFF]); - m.insert("tan", [0xD2, 0xB4, 0x8C, 0xFF]); - m.insert("teal", [0x00, 0x80, 0x80, 0xFF]); - m.insert("thistle", [0xD8, 0xBF, 0xD8, 0xFF]); - m.insert("tomato", [0xFF, 0x63, 0x47, 0xFF]); - m.insert("turquoise", [0x40, 0xE0, 0xD0, 0xFF]); - m.insert("violet", [0xEE, 0x82, 0xEE, 0xFF]); - m.insert("wheat", [0xF5, 0xDE, 0xB3, 0xFF]); - m.insert("white", [0xFF, 0xFF, 0xFF, 0xFF]); - m.insert("whitesmoke", [0xF5, 0xF5, 0xF5, 0xFF]); - m.insert("yellow", [0xFF, 0xFF, 0x00, 0xFF]); - m.insert("yellowgreen", [0x9A, 0xCD, 0x32, 0xFF]); - m.insert("transparent", [0x00, 0x00, 0x00, 0x00]); + m.insert( + InternedString::get_or_intern("aliceblue"), + [0xF0, 0xF8, 0xFF, 0xFF], + ); + m.insert( + InternedString::get_or_intern("antiquewhite"), + [0xFA, 0xEB, 0xD7, 0xFF], + ); + m.insert( + InternedString::get_or_intern("aqua"), + [0x00, 0xFF, 0xFF, 0xFF], + ); + m.insert( + InternedString::get_or_intern("aquamarine"), + [0x7F, 0xFF, 0xD4, 0xFF], + ); + m.insert( + InternedString::get_or_intern("azure"), + [0xF0, 0xFF, 0xFF, 0xFF], + ); + m.insert( + InternedString::get_or_intern("beige"), + [0xF5, 0xF5, 0xDC, 0xFF], + ); + m.insert( + InternedString::get_or_intern("bisque"), + [0xFF, 0xE4, 0xC4, 0xFF], + ); + m.insert( + InternedString::get_or_intern("black"), + [0x00, 0x00, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("blanchedalmond"), + [0xFF, 0xEB, 0xCD, 0xFF], + ); + m.insert( + InternedString::get_or_intern("blue"), + [0x00, 0x00, 0xFF, 0xFF], + ); + m.insert( + InternedString::get_or_intern("blueviolet"), + [0x8A, 0x2B, 0xE2, 0xFF], + ); + m.insert( + InternedString::get_or_intern("brown"), + [0xA5, 0x2A, 0x2A, 0xFF], + ); + m.insert( + InternedString::get_or_intern("burlywood"), + [0xDE, 0xB8, 0x87, 0xFF], + ); + m.insert( + InternedString::get_or_intern("cadetblue"), + [0x5F, 0x9E, 0xA0, 0xFF], + ); + m.insert( + InternedString::get_or_intern("chartreuse"), + [0x7F, 0xFF, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("chocolate"), + [0xD2, 0x69, 0x1E, 0xFF], + ); + m.insert( + InternedString::get_or_intern("coral"), + [0xFF, 0x7F, 0x50, 0xFF], + ); + m.insert( + InternedString::get_or_intern("cornflowerblue"), + [0x64, 0x95, 0xED, 0xFF], + ); + m.insert( + InternedString::get_or_intern("cornsilk"), + [0xFF, 0xF8, 0xDC, 0xFF], + ); + m.insert( + InternedString::get_or_intern("crimson"), + [0xDC, 0x14, 0x3C, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkblue"), + [0x00, 0x00, 0x8B, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkcyan"), + [0x00, 0x8B, 0x8B, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkgoldenrod"), + [0xB8, 0x86, 0x0B, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkgray"), + [0xA9, 0xA9, 0xA9, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkgreen"), + [0x00, 0x64, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkkhaki"), + [0xBD, 0xB7, 0x6B, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkmagenta"), + [0x8B, 0x00, 0x8B, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkolivegreen"), + [0x55, 0x6B, 0x2F, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkorange"), + [0xFF, 0x8C, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkorchid"), + [0x99, 0x32, 0xCC, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkred"), + [0x8B, 0x00, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darksalmon"), + [0xE9, 0x96, 0x7A, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkseagreen"), + [0x8F, 0xBC, 0x8F, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkslateblue"), + [0x48, 0x3D, 0x8B, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkslategray"), + [0x2F, 0x4F, 0x4F, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkturquoise"), + [0x00, 0xCE, 0xD1, 0xFF], + ); + m.insert( + InternedString::get_or_intern("darkviolet"), + [0x94, 0x00, 0xD3, 0xFF], + ); + m.insert( + InternedString::get_or_intern("deeppink"), + [0xFF, 0x14, 0x93, 0xFF], + ); + m.insert( + InternedString::get_or_intern("deepskyblue"), + [0x00, 0xBF, 0xFF, 0xFF], + ); + m.insert( + InternedString::get_or_intern("dimgray"), + [0x69, 0x69, 0x69, 0xFF], + ); + m.insert( + InternedString::get_or_intern("dodgerblue"), + [0x1E, 0x90, 0xFF, 0xFF], + ); + m.insert( + InternedString::get_or_intern("firebrick"), + [0xB2, 0x22, 0x22, 0xFF], + ); + m.insert( + InternedString::get_or_intern("floralwhite"), + [0xFF, 0xFA, 0xF0, 0xFF], + ); + m.insert( + InternedString::get_or_intern("forestgreen"), + [0x22, 0x8B, 0x22, 0xFF], + ); + m.insert( + InternedString::get_or_intern("fuchsia"), + [0xFF, 0x00, 0xFF, 0xFF], + ); + m.insert( + InternedString::get_or_intern("gainsboro"), + [0xDC, 0xDC, 0xDC, 0xFF], + ); + m.insert( + InternedString::get_or_intern("ghostwhite"), + [0xF8, 0xF8, 0xFF, 0xFF], + ); + m.insert( + InternedString::get_or_intern("gold"), + [0xFF, 0xD7, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("goldenrod"), + [0xDA, 0xA5, 0x20, 0xFF], + ); + m.insert( + InternedString::get_or_intern("gray"), + [0x80, 0x80, 0x80, 0xFF], + ); + m.insert( + InternedString::get_or_intern("green"), + [0x00, 0x80, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("greenyellow"), + [0xAD, 0xFF, 0x2F, 0xFF], + ); + m.insert( + InternedString::get_or_intern("honeydew"), + [0xF0, 0xFF, 0xF0, 0xFF], + ); + m.insert( + InternedString::get_or_intern("hotpink"), + [0xFF, 0x69, 0xB4, 0xFF], + ); + m.insert( + InternedString::get_or_intern("indianred"), + [0xCD, 0x5C, 0x5C, 0xFF], + ); + m.insert( + InternedString::get_or_intern("indigo"), + [0x4B, 0x00, 0x82, 0xFF], + ); + m.insert( + InternedString::get_or_intern("ivory"), + [0xFF, 0xFF, 0xF0, 0xFF], + ); + m.insert( + InternedString::get_or_intern("khaki"), + [0xF0, 0xE6, 0x8C, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lavender"), + [0xE6, 0xE6, 0xFA, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lavenderblush"), + [0xFF, 0xF0, 0xF5, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lawngreen"), + [0x7C, 0xFC, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lemonchiffon"), + [0xFF, 0xFA, 0xCD, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightblue"), + [0xAD, 0xD8, 0xE6, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightcoral"), + [0xF0, 0x80, 0x80, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightcyan"), + [0xE0, 0xFF, 0xFF, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightgoldenrodyellow"), + [0xFA, 0xFA, 0xD2, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightgray"), + [0xD3, 0xD3, 0xD3, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightgreen"), + [0x90, 0xEE, 0x90, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightpink"), + [0xFF, 0xB6, 0xC1, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightsalmon"), + [0xFF, 0xA0, 0x7A, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightseagreen"), + [0x20, 0xB2, 0xAA, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightskyblue"), + [0x87, 0xCE, 0xFA, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightslategray"), + [0x77, 0x88, 0x99, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightsteelblue"), + [0xB0, 0xC4, 0xDE, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lightyellow"), + [0xFF, 0xFF, 0xE0, 0xFF], + ); + m.insert( + InternedString::get_or_intern("lime"), + [0x00, 0xFF, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("limegreen"), + [0x32, 0xCD, 0x32, 0xFF], + ); + m.insert( + InternedString::get_or_intern("linen"), + [0xFA, 0xF0, 0xE6, 0xFF], + ); + m.insert( + InternedString::get_or_intern("maroon"), + [0x80, 0x00, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("mediumaquamarine"), + [0x66, 0xCD, 0xAA, 0xFF], + ); + m.insert( + InternedString::get_or_intern("mediumblue"), + [0x00, 0x00, 0xCD, 0xFF], + ); + m.insert( + InternedString::get_or_intern("mediumorchid"), + [0xBA, 0x55, 0xD3, 0xFF], + ); + m.insert( + InternedString::get_or_intern("mediumpurple"), + [0x93, 0x70, 0xDB, 0xFF], + ); + m.insert( + InternedString::get_or_intern("mediumseagreen"), + [0x3C, 0xB3, 0x71, 0xFF], + ); + m.insert( + InternedString::get_or_intern("mediumslateblue"), + [0x7B, 0x68, 0xEE, 0xFF], + ); + m.insert( + InternedString::get_or_intern("mediumspringgreen"), + [0x00, 0xFA, 0x9A, 0xFF], + ); + m.insert( + InternedString::get_or_intern("mediumturquoise"), + [0x48, 0xD1, 0xCC, 0xFF], + ); + m.insert( + InternedString::get_or_intern("mediumvioletred"), + [0xC7, 0x15, 0x85, 0xFF], + ); + m.insert( + InternedString::get_or_intern("midnightblue"), + [0x19, 0x19, 0x70, 0xFF], + ); + m.insert( + InternedString::get_or_intern("mintcream"), + [0xF5, 0xFF, 0xFA, 0xFF], + ); + m.insert( + InternedString::get_or_intern("mistyrose"), + [0xFF, 0xE4, 0xE1, 0xFF], + ); + m.insert( + InternedString::get_or_intern("moccasin"), + [0xFF, 0xE4, 0xB5, 0xFF], + ); + m.insert( + InternedString::get_or_intern("navajowhite"), + [0xFF, 0xDE, 0xAD, 0xFF], + ); + m.insert( + InternedString::get_or_intern("navy"), + [0x00, 0x00, 0x80, 0xFF], + ); + m.insert( + InternedString::get_or_intern("oldlace"), + [0xFD, 0xF5, 0xE6, 0xFF], + ); + m.insert( + InternedString::get_or_intern("olive"), + [0x80, 0x80, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("olivedrab"), + [0x6B, 0x8E, 0x23, 0xFF], + ); + m.insert( + InternedString::get_or_intern("orange"), + [0xFF, 0xA5, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("orangered"), + [0xFF, 0x45, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("orchid"), + [0xDA, 0x70, 0xD6, 0xFF], + ); + m.insert( + InternedString::get_or_intern("palegoldenrod"), + [0xEE, 0xE8, 0xAA, 0xFF], + ); + m.insert( + InternedString::get_or_intern("palegreen"), + [0x98, 0xFB, 0x98, 0xFF], + ); + m.insert( + InternedString::get_or_intern("paleturquoise"), + [0xAF, 0xEE, 0xEE, 0xFF], + ); + m.insert( + InternedString::get_or_intern("palevioletred"), + [0xDB, 0x70, 0x93, 0xFF], + ); + m.insert( + InternedString::get_or_intern("papayawhip"), + [0xFF, 0xEF, 0xD5, 0xFF], + ); + m.insert( + InternedString::get_or_intern("peachpuff"), + [0xFF, 0xDA, 0xB9, 0xFF], + ); + m.insert( + InternedString::get_or_intern("peru"), + [0xCD, 0x85, 0x3F, 0xFF], + ); + m.insert( + InternedString::get_or_intern("pink"), + [0xFF, 0xC0, 0xCB, 0xFF], + ); + m.insert( + InternedString::get_or_intern("plum"), + [0xDD, 0xA0, 0xDD, 0xFF], + ); + m.insert( + InternedString::get_or_intern("powderblue"), + [0xB0, 0xE0, 0xE6, 0xFF], + ); + m.insert( + InternedString::get_or_intern("purple"), + [0x80, 0x00, 0x80, 0xFF], + ); + m.insert( + InternedString::get_or_intern("rebeccapurple"), + [0x66, 0x33, 0x99, 0xFF], + ); + m.insert( + InternedString::get_or_intern("red"), + [0xFF, 0x00, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("rosybrown"), + [0xBC, 0x8F, 0x8F, 0xFF], + ); + m.insert( + InternedString::get_or_intern("royalblue"), + [0x41, 0x69, 0xE1, 0xFF], + ); + m.insert( + InternedString::get_or_intern("saddlebrown"), + [0x8B, 0x45, 0x13, 0xFF], + ); + m.insert( + InternedString::get_or_intern("salmon"), + [0xFA, 0x80, 0x72, 0xFF], + ); + m.insert( + InternedString::get_or_intern("sandybrown"), + [0xF4, 0xA4, 0x60, 0xFF], + ); + m.insert( + InternedString::get_or_intern("seagreen"), + [0x2E, 0x8B, 0x57, 0xFF], + ); + m.insert( + InternedString::get_or_intern("seashell"), + [0xFF, 0xF5, 0xEE, 0xFF], + ); + m.insert( + InternedString::get_or_intern("sienna"), + [0xA0, 0x52, 0x2D, 0xFF], + ); + m.insert( + InternedString::get_or_intern("silver"), + [0xC0, 0xC0, 0xC0, 0xFF], + ); + m.insert( + InternedString::get_or_intern("skyblue"), + [0x87, 0xCE, 0xEB, 0xFF], + ); + m.insert( + InternedString::get_or_intern("slateblue"), + [0x6A, 0x5A, 0xCD, 0xFF], + ); + m.insert( + InternedString::get_or_intern("slategray"), + [0x70, 0x80, 0x90, 0xFF], + ); + m.insert( + InternedString::get_or_intern("snow"), + [0xFF, 0xFA, 0xFA, 0xFF], + ); + m.insert( + InternedString::get_or_intern("springgreen"), + [0x00, 0xFF, 0x7F, 0xFF], + ); + m.insert( + InternedString::get_or_intern("steelblue"), + [0x46, 0x82, 0xB4, 0xFF], + ); + m.insert( + InternedString::get_or_intern("tan"), + [0xD2, 0xB4, 0x8C, 0xFF], + ); + m.insert( + InternedString::get_or_intern("teal"), + [0x00, 0x80, 0x80, 0xFF], + ); + m.insert( + InternedString::get_or_intern("thistle"), + [0xD8, 0xBF, 0xD8, 0xFF], + ); + m.insert( + InternedString::get_or_intern("tomato"), + [0xFF, 0x63, 0x47, 0xFF], + ); + m.insert( + InternedString::get_or_intern("turquoise"), + [0x40, 0xE0, 0xD0, 0xFF], + ); + m.insert( + InternedString::get_or_intern("violet"), + [0xEE, 0x82, 0xEE, 0xFF], + ); + m.insert( + InternedString::get_or_intern("wheat"), + [0xF5, 0xDE, 0xB3, 0xFF], + ); + m.insert( + InternedString::get_or_intern("white"), + [0xFF, 0xFF, 0xFF, 0xFF], + ); + m.insert( + InternedString::get_or_intern("whitesmoke"), + [0xF5, 0xF5, 0xF5, 0xFF], + ); + m.insert( + InternedString::get_or_intern("yellow"), + [0xFF, 0xFF, 0x00, 0xFF], + ); + m.insert( + InternedString::get_or_intern("yellowgreen"), + [0x9A, 0xCD, 0x32, 0xFF], + ); + m.insert( + InternedString::get_or_intern("transparent"), + [0x00, 0x00, 0x00, 0x00], + ); m }); diff --git a/src/common.rs b/src/common.rs index d01d51a..dcecdf7 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,3 +1,4 @@ +use crate::interner::InternedString; use std::fmt::{self, Display, Write}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -119,3 +120,38 @@ impl Display for QualifiedName { f.write_str(&self.ident) } } + +#[derive(Debug, Clone, Hash, Eq, PartialEq, Copy)] +pub(crate) struct Identifier(InternedString); + +impl Into for InternedString { + fn into(self) -> Identifier { + Identifier(InternedString::get_or_intern( + self.resolve().replace('_', "-"), + )) + } +} + +impl From for Identifier { + fn from(s: String) -> Identifier { + Identifier(InternedString::get_or_intern(s.replace('_', "-"))) + } +} + +impl Into for &String { + fn into(self) -> Identifier { + Identifier(InternedString::get_or_intern(self.replace('_', "-"))) + } +} + +impl Into for &str { + fn into(self) -> Identifier { + Identifier(InternedString::get_or_intern(self.replace('_', "-"))) + } +} + +impl Display for Identifier { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/src/interner.rs b/src/interner.rs new file mode 100644 index 0000000..b1255d6 --- /dev/null +++ b/src/interner.rs @@ -0,0 +1,55 @@ +use lasso::{Rodeo, Spur}; + +use std::cell::RefCell; +use std::fmt::{self, Display}; + +thread_local!(static STRINGS: RefCell> = RefCell::new(Rodeo::default())); + +use keywords::EMPTY_STRING; + +pub(crate) mod keywords { + use super::InternedString; + use once_cell::sync::Lazy; + macro_rules! keyword { + ($ident:ident, $val:literal) => { + pub(crate) static $ident: Lazy = + Lazy::new(|| InternedString::get_or_intern($val)); + }; + } + + keyword!(EMPTY_STRING, ""); + keyword!(TRUE, "true"); + keyword!(FALSE, "false"); + keyword!(AND, "and"); + keyword!(OR, "or"); + keyword!(NOT, "not"); + keyword!(NULL, "null"); + keyword!(CALC, "calc"); + keyword!(URL, "url"); + keyword!(PROGID, "progid"); + keyword!(ELEMENT, "element"); + keyword!(EXPRESSION, "expression"); +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) struct InternedString(Spur); + +impl InternedString { + pub fn get_or_intern>(s: T) -> Self { + Self(STRINGS.with(|interner| interner.borrow_mut().get_or_intern(s))) + } + + pub fn resolve(&self) -> String { + STRINGS.with(|interner| interner.borrow().resolve(&self.0).to_string()) + } + + pub fn is_empty(self) -> bool { + self == *EMPTY_STRING + } +} + +impl Display for InternedString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.resolve()) + } +} diff --git a/src/lib.rs b/src/lib.rs index d34c7f5..49cbe2c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,7 @@ use peekmore::{PeekMore, PeekMoreIterator}; use crate::atrule::{AtRule, AtRuleKind, Function, Mixin}; pub use crate::error::{SassError, SassResult}; +use crate::interner::keywords::EMPTY_STRING; use crate::scope::{insert_global_var, Scope}; use crate::selector::Selector; use crate::style::Style; @@ -109,6 +110,7 @@ mod color; mod common; mod error; mod imports; +mod interner; mod lexer; mod output; mod scope; @@ -198,7 +200,7 @@ pub(crate) fn eat_expr>( &mut values.into_iter().peekmore(), scope, super_selector, - String::new(), + *EMPTY_STRING, tok.pos, )?; return Ok(Some(Spanned { @@ -218,7 +220,7 @@ pub(crate) fn eat_expr>( devour_whitespace(toks); return Ok(Some(Spanned { node: Expr::Style(Box::new(Style { - property: String::new(), + property: *EMPTY_STRING, value: Value::Null.span(span), })), span, @@ -228,7 +230,7 @@ pub(crate) fn eat_expr>( &mut v, scope, super_selector, - String::new(), + *EMPTY_STRING, span_before, )?; let value = Style::parse_value(&mut v, scope, super_selector)?; @@ -255,7 +257,7 @@ pub(crate) fn eat_expr>( &mut v, scope, super_selector, - String::new(), + *EMPTY_STRING, tok.pos, )?; let value = Style::parse_value(&mut v, scope, super_selector)?; diff --git a/src/scope.rs b/src/scope.rs index 54a6751..509c812 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -4,66 +4,70 @@ 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 = RefCell::new(Scope::new())); -pub(crate) fn get_global_var(s: Spanned) -> SassResult> { - GLOBAL_SCOPE.with(|scope| match scope.borrow().vars().get(&s.node) { +pub(crate) fn get_global_var>(s: Spanned) -> SassResult> { + 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('_', "-"))) +/// Returns true if a variable exists in the *global* scope +pub(crate) fn global_var_exists>(v: T) -> bool { + GLOBAL_SCOPE.with(|scope| scope.borrow().vars().contains_key(&v.into())) } -pub(crate) fn insert_global_var(s: &str, v: Spanned) -> SassResult>> { - GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_var(s, v)) +pub(crate) fn insert_global_var>( + s: T, + v: Spanned, +) -> SassResult>> { + GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_var(s.into(), v)) } -pub(crate) fn get_global_fn(s: Spanned) -> SassResult { - GLOBAL_SCOPE.with(|scope| match scope.borrow().functions().get(&s.node) { - Some(v) => Ok(v.clone()), - None => Err(("Undefined function.", s.span).into()), - }) +pub(crate) fn get_global_fn>(s: Spanned) -> SassResult { + 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('_', "-")) - }) +/// Returns true if a function exists in the *global* scope +pub(crate) fn global_fn_exists>(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 { - GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_fn(s, v)) +pub(crate) fn insert_global_fn>(s: T, v: Function) -> Option { + GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_fn(s.into(), v)) } -pub(crate) fn get_global_mixin(s: Spanned) -> SassResult { - GLOBAL_SCOPE.with(|scope| match scope.borrow().mixins().get(&s.node) { +pub(crate) fn get_global_mixin>(s: Spanned) -> SassResult { + 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('_', "-"))) +/// Returns true if a mixin exists in the *global* scope +pub(crate) fn global_mixin_exists>(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 { - GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_mixin(s, v)) +pub(crate) fn insert_global_mixin>(s: T, v: Mixin) -> Option { + GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_mixin(s.into(), v)) } #[derive(Debug, Clone)] pub(crate) struct Scope { - vars: HashMap>, - mixins: HashMap, - functions: HashMap, + vars: HashMap>, + mixins: HashMap, + functions: HashMap, } impl Scope { @@ -76,68 +80,72 @@ impl Scope { } } - pub const fn vars(&self) -> &HashMap> { + pub const fn vars(&self) -> &HashMap> { &self.vars } - pub const fn functions(&self) -> &HashMap { + pub const fn functions(&self) -> &HashMap { &self.functions } - pub const fn mixins(&self) -> &HashMap { + pub const fn mixins(&self) -> &HashMap { &self.mixins } - pub fn get_var(&self, mut name: Spanned) -> SassResult> { - name.node = name.node.replace('_', "-"); + pub fn get_var>(&self, name: Spanned) -> SassResult> { + 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) -> SassResult>> { + pub fn insert_var>( + &mut self, + s: T, + v: Spanned, + ) -> SassResult>> { 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>(&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) -> SassResult { - name.node = name.node.replace('_', "-"); + pub fn get_mixin>(&self, name: Spanned) -> SassResult { + 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 { - self.mixins.insert(s.replace('_', "-"), v) + pub fn insert_mixin>(&mut self, s: T, v: Mixin) -> Option { + 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>(&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) -> SassResult { - name.node = name.node.replace('_', "-"); + pub fn get_fn>(&self, name: Spanned) -> SassResult { + 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 { - self.functions.insert(s.replace('_', "-"), v) + pub fn insert_fn>(&mut self, s: T, v: Function) -> Option { + 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>(&self, v: T) -> bool { + let name = v.into(); + self.functions.contains_key(&name) || global_fn_exists(name) } pub fn extend(&mut self, other: Scope) { diff --git a/src/selector/attribute.rs b/src/selector/attribute.rs index e2b3524..7cb901c 100644 --- a/src/selector/attribute.rs +++ b/src/selector/attribute.rs @@ -7,6 +7,7 @@ use codemap::Span; use super::{Selector, SelectorKind}; use crate::common::{QualifiedName, QuoteKind}; use crate::error::SassResult; +use crate::interner::InternedString; use crate::scope::Scope; use crate::utils::{devour_whitespace, eat_ident, is_ident, parse_quoted_string}; use crate::value::Value; @@ -124,7 +125,7 @@ impl Attribute { q @ '\'' | q @ '"' => { toks.next(); match parse_quoted_string(toks, scope, q, super_selector)?.node { - Value::Ident(s, ..) => s, + Value::Ident(s, ..) => s.resolve().to_string(), _ => unreachable!(), } } @@ -180,9 +181,12 @@ impl Display for Attribute { // or having special emitter for quoted strings? // (also avoids the clone because we can consume/modify self) f.write_str( - &Value::Ident(self.value.clone(), QuoteKind::Quoted) - .to_css_string(self.span) - .unwrap(), + &Value::Ident( + InternedString::get_or_intern(self.value.clone()), + QuoteKind::Quoted, + ) + .to_css_string(self.span) + .unwrap(), )?; // todo: this space is not emitted when `compressed` output if self.modifier.is_some() { diff --git a/src/selector/mod.rs b/src/selector/mod.rs index 013fd87..b88fe67 100644 --- a/src/selector/mod.rs +++ b/src/selector/mod.rs @@ -4,6 +4,7 @@ use peekmore::{PeekMore, PeekMoreIterator}; use crate::common::{Brackets, ListSeparator, QuoteKind}; use crate::error::SassResult; +use crate::interner::InternedString; use crate::scope::Scope; use crate::utils::{ devour_whitespace, eat_comment, eat_ident_no_interpolation, parse_interpolation, @@ -57,7 +58,12 @@ impl SelectorPart { Value::List( kinds .iter() - .map(|s| Value::Ident(s.to_string(), QuoteKind::None)) + .map(|s| { + Value::Ident( + InternedString::get_or_intern(s.to_string()), + QuoteKind::None, + ) + }) .collect(), ListSeparator::Space, Brackets::None, diff --git a/src/style.rs b/src/style.rs index 84e9220..5685b7f 100644 --- a/src/style.rs +++ b/src/style.rs @@ -3,6 +3,7 @@ use peekmore::PeekMoreIterator; use codemap::{Span, Spanned}; use crate::error::SassResult; +use crate::interner::InternedString; use crate::scope::Scope; use crate::selector::Selector; use crate::utils::{devour_whitespace, devour_whitespace_or_comment, eat_ident}; @@ -12,7 +13,7 @@ use crate::{Expr, Token}; /// A style: `color: red` #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct Style { - pub property: String, + pub property: InternedString, pub value: Spanned, } @@ -21,9 +22,9 @@ impl Style { toks: &mut PeekMoreIterator, scope: &Scope, super_selector: &Selector, - super_property: String, + super_property: InternedString, span_before: Span, - ) -> SassResult { + ) -> SassResult { StyleParser::new(scope, super_selector).parse_property(toks, super_property, span_before) } @@ -57,7 +58,7 @@ impl Style { toks: &mut PeekMoreIterator, scope: &Scope, super_selector: &Selector, - super_property: String, + super_property: InternedString, ) -> SassResult { StyleParser::new(scope, super_selector).eat_style_group(toks, super_property, scope) } @@ -88,7 +89,7 @@ impl<'a> StyleParser<'a> { pub(crate) fn eat_style_group>( &self, toks: &mut PeekMoreIterator, - super_property: String, + super_property: InternedString, scope: &Scope, ) -> SassResult { let mut styles = Vec::new(); @@ -99,8 +100,7 @@ impl<'a> StyleParser<'a> { let span_before = toks.next().unwrap().pos; devour_whitespace(toks); loop { - let property = - self.parse_property(toks, super_property.clone(), span_before)?; + let property = self.parse_property(toks, super_property, span_before)?; if let Some(tok) = toks.peek() { if tok.kind == '{' { match self.eat_style_group(toks, property, scope)? { @@ -133,7 +133,7 @@ impl<'a> StyleParser<'a> { } '{' => { styles.push(Style { - property: property.clone(), + property: property, value, }); match self.eat_style_group(toks, property, scope)? { @@ -170,7 +170,7 @@ impl<'a> StyleParser<'a> { } '{' => { let mut v = vec![Style { - property: super_property.clone(), + property: super_property, value, }]; match self.eat_style_group(toks, super_property, scope)? { @@ -195,9 +195,9 @@ impl<'a> StyleParser<'a> { pub(crate) fn parse_property>( &self, toks: &mut PeekMoreIterator, - mut super_property: String, + super_property: InternedString, span_before: Span, - ) -> SassResult { + ) -> SassResult { devour_whitespace(toks); let property = eat_ident(toks, self.scope, self.super_selector, span_before)?; devour_whitespace_or_comment(toks)?; @@ -209,12 +209,13 @@ impl<'a> StyleParser<'a> { } if super_property.is_empty() { - Ok(property.node) + Ok(InternedString::get_or_intern(property.node)) } else { + let mut super_property = super_property.resolve().to_string(); super_property.reserve(1 + property.node.len()); super_property.push('-'); super_property.push_str(&property.node); - Ok(super_property) + Ok(InternedString::get_or_intern(super_property)) } } } diff --git a/src/stylesheet.rs b/src/stylesheet.rs index 3fb6e0b..cd98947 100644 --- a/src/stylesheet.rs +++ b/src/stylesheet.rs @@ -10,6 +10,7 @@ use peekmore::{PeekMore, PeekMoreIterator}; use wasm_bindgen::prelude::*; use crate::atrule::{eat_include, AtRule, AtRuleKind}; +use crate::common::Identifier; use crate::error::{SassError, SassResult}; use crate::imports::import; use crate::lexer::Lexer; @@ -164,8 +165,9 @@ impl<'a> StyleSheetParser<'a> { let mut rules: Vec> = 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,15 +179,18 @@ 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())?; + let name = name.map_node(|n| Identifier::from(n)); - if !(default && global_var_exists(&name)) { - insert_global_var(&name.node, val)?; + let VariableDecl { val, default, .. } = + eat_variable_value(self.lexer, &Scope::new(), &Selector::new())?; + + if !(default && global_var_exists(name.node)) { + insert_global_var(name.node, val)?; } } Some(..) | None => return Err(("expected \":\".", name.span).into()), @@ -202,17 +207,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 +225,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 +241,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 +258,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 +289,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 +326,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()); diff --git a/src/utils/strings.rs b/src/utils/strings.rs index b0b7116..3e11d72 100644 --- a/src/utils/strings.rs +++ b/src/utils/strings.rs @@ -6,6 +6,7 @@ use peekmore::PeekMoreIterator; use crate::common::QuoteKind; use crate::error::SassResult; +use crate::interner::InternedString; use crate::selector::Selector; use crate::value::Value; use crate::{Scope, Token}; @@ -213,7 +214,7 @@ pub(crate) fn eat_ident>( toks.next(); text.push_str( &match parse_interpolation(toks, scope, super_selector)?.node { - Value::Ident(s, ..) => s, + Value::Ident(s, ..) => s.resolve().to_string(), v => v.to_css_string(span)?.into(), }, ); @@ -291,7 +292,7 @@ pub(crate) fn parse_quoted_string>( toks.next(); let interpolation = parse_interpolation(toks, scope, super_selector)?; s.push_str(&match interpolation.node { - Value::Ident(s, ..) => s, + Value::Ident(s, ..) => s.resolve().to_string(), v => v.to_css_string(interpolation.span)?.into(), }); continue; @@ -345,7 +346,7 @@ pub(crate) fn parse_quoted_string>( } } Ok(Spanned { - node: Value::Ident(s, QuoteKind::Quoted), + node: Value::Ident(InternedString::get_or_intern(s), QuoteKind::Quoted), span, }) } diff --git a/src/value/css_function.rs b/src/value/css_function.rs index b543e90..f985750 100644 --- a/src/value/css_function.rs +++ b/src/value/css_function.rs @@ -113,7 +113,7 @@ pub(crate) fn try_eat_url>( let (interpolation, count) = peek_interpolation(toks, scope, super_selector)?; peek_counter += count; buf.push_str(&match interpolation.node { - Value::Ident(s, ..) => s, + Value::Ident(s, ..) => s.resolve().to_string(), v => v.to_css_string(interpolation.span)?.into(), }); } else { diff --git a/src/value/mod.rs b/src/value/mod.rs index 8e1ecf2..f7ff32f 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -6,6 +6,7 @@ use codemap::{Span, Spanned}; use crate::color::Color; use crate::common::{Brackets, ListSeparator, Op, QuoteKind}; use crate::error::SassResult; +use crate::interner::InternedString; use crate::unit::Unit; use crate::utils::hex_char_for; @@ -33,7 +34,7 @@ pub(crate) enum Value { UnaryOp(Op, Box), BinaryOp(Box, Op, Box), Paren(Box), - Ident(String, QuoteKind), + Ident(InternedString, QuoteKind), Map(SassMap), ArgList(Vec>), /// Returned by `get-function()` @@ -167,6 +168,7 @@ impl Value { } Self::Paren(val) => val.to_css_string(span)?, Self::Ident(string, QuoteKind::None) => { + let string = string.resolve(); let mut after_newline = false; let mut buf = String::with_capacity(string.len()); for c in string.chars() { @@ -189,8 +191,9 @@ impl Value { Cow::Owned(buf) } Self::Ident(string, QuoteKind::Quoted) => { + let string = string.resolve(); let mut buf = String::with_capacity(string.len()); - visit_quoted_string(&mut buf, false, string)?; + visit_quoted_string(&mut buf, false, &string)?; Cow::Owned(buf) } Self::True => Cow::Borrowed("true"), @@ -249,7 +252,7 @@ impl Value { pub fn is_special_function(&self) -> bool { match self { - Self::Ident(s, QuoteKind::None) => is_special_function(s), + Self::Ident(s, QuoteKind::None) => is_special_function(&s.resolve()), _ => false, } } diff --git a/src/value/ops.rs b/src/value/ops.rs index 9829c51..c28fad6 100644 --- a/src/value/ops.rs +++ b/src/value/ops.rs @@ -4,6 +4,7 @@ use codemap::{Span, Spanned}; use crate::common::{Op, QuoteKind}; use crate::error::SassResult; +use crate::interner::InternedString; use crate::unit::{Unit, UNIT_CONVERSION_TABLE}; use crate::value::Value; @@ -111,7 +112,10 @@ impl Value { pub fn unary_op_plus(self, span: Span) -> SassResult { Ok(match self.eval(span)?.node { v @ Value::Dimension(..) => v, - v => Value::Ident(format!("+{}", v.to_css_string(span)?), QuoteKind::None), + v => Value::Ident( + InternedString::get_or_intern(format!("+{}", v.to_css_string(span)?)), + QuoteKind::None, + ), }) } @@ -259,22 +263,28 @@ impl Value { Self::Function(..) | Self::ArgList(..) | Self::Map(..) => todo!(), Self::Important | Self::True | Self::False => match other { Self::Ident(s, QuoteKind::Quoted) => Value::Ident( - format!("{}{}", self.to_css_string(span)?, s), + InternedString::get_or_intern(format!("{}{}", self.to_css_string(span)?, s)), QuoteKind::Quoted, ), - Self::Null => Value::Ident(self.to_css_string(span)?.into(), QuoteKind::None), + Self::Null => Value::Ident( + InternedString::get_or_intern(self.to_css_string(span)?.into_owned()), + QuoteKind::None, + ), _ => Value::Ident( - format!( + InternedString::get_or_intern(format!( "{}{}", self.to_css_string(span)?, other.to_css_string(span)? - ), + )), QuoteKind::None, ), }, Self::Null => match other { Self::Null => Self::Null, - _ => Value::Ident(other.to_css_string(span)?.into(), QuoteKind::None), + _ => Value::Ident( + InternedString::get_or_intern(other.to_css_string(span)?.into_owned()), + QuoteKind::None, + ), }, Self::Dimension(num, unit) => match other { Self::Dimension(num2, unit2) => { @@ -299,14 +309,30 @@ impl Value { ) } } - Self::Ident(s, q) => Value::Ident(format!("{}{}{}", num, unit, s), q), - Self::Null => Value::Ident(format!("{}{}", num, unit), QuoteKind::None), + Self::Ident(s, q) => Value::Ident( + InternedString::get_or_intern(format!("{}{}{}", num, unit, s)), + q, + ), + Self::Null => Value::Ident( + InternedString::get_or_intern(format!("{}{}", num, unit)), + QuoteKind::None, + ), Self::List(..) => Value::Ident( - format!("{}{}{}", num, unit, other.to_css_string(span)?), + InternedString::get_or_intern(format!( + "{}{}{}", + num, + unit, + other.to_css_string(span)? + )), QuoteKind::None, ), Self::True | Self::False => Self::Ident( - format!("{}{}{}", num, unit, other.to_css_string(span)?), + InternedString::get_or_intern(format!( + "{}{}{}", + num, + unit, + other.to_css_string(span)? + )), QuoteKind::None, ), _ => { @@ -323,10 +349,15 @@ impl Value { } }, Self::Color(c) => match other { - Self::Ident(s, q) => Value::Ident(format!("{}{}", c, s), q), - Self::Null => Value::Ident(c.to_string(), QuoteKind::None), + Self::Ident(s, q) => { + Value::Ident(InternedString::get_or_intern(format!("{}{}", c, s)), q) + } + Self::Null => Value::Ident( + InternedString::get_or_intern(c.to_string()), + QuoteKind::None, + ), Self::List(..) => Value::Ident( - format!("{}{}", c, other.to_css_string(span)?), + InternedString::get_or_intern(format!("{}{}", c, other.to_css_string(span)?)), QuoteKind::None, ), _ => { @@ -363,18 +394,27 @@ impl Value { } Self::UnaryOp(..) | Self::Paren(..) => self.eval(span)?.node.add(other, span)?, Self::Ident(text, quotes) => match other { - Self::Ident(text2, ..) => Self::Ident(text + &text2, quotes), - _ => Value::Ident(text + &other.to_css_string(span)?, quotes), + Self::Ident(text2, ..) => Self::Ident( + InternedString::get_or_intern(text.resolve() + &text2.resolve()), + quotes, + ), + _ => Value::Ident( + InternedString::get_or_intern(text.resolve() + &other.to_css_string(span)?), + quotes, + ), }, Self::List(..) => match other { - Self::Ident(s, q) => Value::Ident(format!("{}{}", self.to_css_string(span)?, s), q), + Self::Ident(s, q) => Value::Ident( + InternedString::get_or_intern(format!("{}{}", self.to_css_string(span)?, s)), + q, + ), Self::Paren(..) => (self.add(other.eval(span)?.node, span))?, _ => Value::Ident( - format!( + InternedString::get_or_intern(format!( "{}{}", self.to_css_string(span)?, other.to_css_string(span)? - ), + )), QuoteKind::None, ), }, @@ -412,20 +452,34 @@ impl Value { } } Self::List(..) => Value::Ident( - format!("{}{}-{}", num, unit, other.to_css_string(span)?), + InternedString::get_or_intern(format!( + "{}{}-{}", + num, + unit, + other.to_css_string(span)? + )), QuoteKind::None, ), Self::Ident(..) => Value::Ident( - format!("{}{}-{}", num, unit, other.to_css_string(span)?), + InternedString::get_or_intern(format!( + "{}{}-{}", + num, + unit, + other.to_css_string(span)? + )), QuoteKind::None, ), _ => todo!(), }, Self::Color(c) => match other { - Self::Ident(s, q) => { - Value::Ident(format!("{}-{}{}{}", c, q, s, q), QuoteKind::None) - } - Self::Null => Value::Ident(format!("{}-", c), QuoteKind::None), + Self::Ident(s, q) => Value::Ident( + InternedString::get_or_intern(format!("{}-{}{}{}", c, q, s, q)), + QuoteKind::None, + ), + Self::Null => Value::Ident( + InternedString::get_or_intern(format!("{}-", c)), + QuoteKind::None, + ), Self::Dimension(..) | Self::Color(..) => { return Err(( format!( @@ -438,7 +492,7 @@ impl Value { .into()) } _ => Value::Ident( - format!("{}-{}", c, other.to_css_string(span)?), + InternedString::get_or_intern(format!("{}-{}", c, other.to_css_string(span)?)), QuoteKind::None, ), }, @@ -464,41 +518,54 @@ impl Value { } Self::Paren(..) => self.eval(span)?.node.sub(other, span)?, Self::Ident(..) => Self::Ident( - format!( + InternedString::get_or_intern(format!( "{}-{}", self.to_css_string(span)?, other.to_css_string(span)? - ), + )), QuoteKind::None, ), Self::List(..) => match other { Self::Ident(s, q) => Value::Ident( - format!("{}-{}{}{}", self.to_css_string(span)?, q, s, q), + InternedString::get_or_intern(format!( + "{}-{}{}{}", + self.to_css_string(span)?, + q, + s, + q + )), QuoteKind::None, ), _ => Value::Ident( - format!( + InternedString::get_or_intern(format!( "{}-{}", self.to_css_string(span)?, other.to_css_string(span)? - ), + )), QuoteKind::None, ), }, _ => match other { Self::Ident(s, q) => Value::Ident( - format!("{}-{}{}{}", self.to_css_string(span)?, q, s, q), + InternedString::get_or_intern(format!( + "{}-{}{}{}", + self.to_css_string(span)?, + q, + s, + q + )), + QuoteKind::None, + ), + Self::Null => Value::Ident( + InternedString::get_or_intern(format!("{}-", self.to_css_string(span)?)), QuoteKind::None, ), - Self::Null => { - Value::Ident(format!("{}-", self.to_css_string(span)?), QuoteKind::None) - } _ => Value::Ident( - format!( + InternedString::get_or_intern(format!( "{}-{}", self.to_css_string(span)?, other.to_css_string(span)? - ), + )), QuoteKind::None, ), }, @@ -604,19 +671,24 @@ impl Value { ) } } - Self::Ident(s, q) => { - Value::Ident(format!("{}{}/{}{}{}", num, unit, q, s, q), QuoteKind::None) - } + Self::Ident(s, q) => Value::Ident( + InternedString::get_or_intern(format!("{}{}/{}{}{}", num, unit, q, s, q)), + QuoteKind::None, + ), Self::BinaryOp(..) | Self::Paren(..) => { Self::Dimension(num, unit).div(other.eval(span)?.node, span)? } _ => todo!(), }, Self::Color(c) => match other { - Self::Ident(s, q) => { - Value::Ident(format!("{}/{}{}{}", c, q, s, q), QuoteKind::None) - } - Self::Null => Value::Ident(format!("{}/", c), QuoteKind::None), + Self::Ident(s, q) => Value::Ident( + InternedString::get_or_intern(format!("{}/{}{}{}", c, q, s, q)), + QuoteKind::None, + ), + Self::Null => Value::Ident( + InternedString::get_or_intern(format!("{}/", c)), + QuoteKind::None, + ), Self::Dimension(..) | Self::Color(..) => { return Err(( format!( @@ -629,7 +701,7 @@ impl Value { .into()) } _ => Value::Ident( - format!("{}/{}", c, other.to_css_string(span)?), + InternedString::get_or_intern(format!("{}/{}", c, other.to_css_string(span)?)), QuoteKind::None, ), }, @@ -656,7 +728,7 @@ impl Value { Self::Paren(..) => self.eval(span)?.node.div(other, span)?, Self::Ident(s1, q1) => match other { Self::Ident(s2, q2) => Value::Ident( - format!("{}{}{}/{}{}{}", q1, s1, q1, q2, s2, q2), + InternedString::get_or_intern(format!("{}{}{}/{}{}{}", q1, s1, q1, q2, s2, q2)), QuoteKind::None, ), Self::Important @@ -664,26 +736,42 @@ impl Value { | Self::False | Self::Dimension(..) | Self::Color(..) => Value::Ident( - format!("{}{}{}/{}", q1, s1, q1, other.to_css_string(span)?), + InternedString::get_or_intern(format!( + "{}{}{}/{}", + q1, + s1, + q1, + other.to_css_string(span)? + )), + QuoteKind::None, + ), + Self::Null => Value::Ident( + InternedString::get_or_intern(format!("{}{}{}/", q1, s1, q1)), QuoteKind::None, ), - Self::Null => Value::Ident(format!("{}{}{}/", q1, s1, q1), QuoteKind::None), _ => todo!(), }, _ => match other { Self::Ident(s, q) => Value::Ident( - format!("{}/{}{}{}", self.to_css_string(span)?, q, s, q), + InternedString::get_or_intern(format!( + "{}/{}{}{}", + self.to_css_string(span)?, + q, + s, + q + )), + QuoteKind::None, + ), + Self::Null => Value::Ident( + InternedString::get_or_intern(format!("{}/", self.to_css_string(span)?)), QuoteKind::None, ), - Self::Null => { - Value::Ident(format!("{}/", self.to_css_string(span)?), QuoteKind::None) - } _ => Value::Ident( - format!( + InternedString::get_or_intern(format!( "{}/{}", self.to_css_string(span)?, other.to_css_string(span)? - ), + )), QuoteKind::None, ), }, @@ -736,7 +824,10 @@ impl Value { pub fn neg(self, span: Span) -> SassResult { Ok(match self.eval(span)?.node { Value::Dimension(n, u) => Value::Dimension(-n, u), - v => Value::Ident(format!("-{}", v.to_css_string(span)?), QuoteKind::None), + v => Value::Ident( + InternedString::get_or_intern(format!("-{}", v.to_css_string(span)?)), + QuoteKind::None, + ), }) } } diff --git a/src/value/parse.rs b/src/value/parse.rs index 7597b7b..2928356 100644 --- a/src/value/parse.rs +++ b/src/value/parse.rs @@ -16,6 +16,7 @@ use crate::builtin::GLOBAL_FUNCTIONS; use crate::color::{Color, NAMED_COLORS}; use crate::common::{Brackets, ListSeparator, Op, QuoteKind}; use crate::error::SassResult; +use crate::interner::{keywords, InternedString}; use crate::scope::Scope; use crate::selector::Selector; use crate::unit::Unit; @@ -58,7 +59,10 @@ fn parse_hex>( span = span.merge(i.span); } else { return Ok(Spanned { - node: Value::Ident(format!("#{}", i.node), QuoteKind::None), + node: Value::Ident( + InternedString::get_or_intern(format!("#{}", i.node)), + QuoteKind::None, + ), span: i.span, }); } @@ -67,7 +71,13 @@ fn parse_hex>( 3 => { let v = match u16::from_str_radix(&s, 16) { Ok(a) => a, - Err(_) => return Ok(Value::Ident(format!("#{}", s), QuoteKind::None).span(span)), + Err(_) => { + return Ok(Value::Ident( + InternedString::get_or_intern(format!("#{}", s)), + QuoteKind::None, + ) + .span(span)) + } }; let red = (((v & 0xf00) >> 8) * 0x11) as u8; let green = (((v & 0x0f0) >> 4) * 0x11) as u8; @@ -80,7 +90,13 @@ fn parse_hex>( 4 => { let v = match u16::from_str_radix(&s, 16) { Ok(a) => a, - Err(_) => return Ok(Value::Ident(format!("#{}", s), QuoteKind::None).span(span)), + Err(_) => { + return Ok(Value::Ident( + InternedString::get_or_intern(format!("#{}", s)), + QuoteKind::None, + ) + .span(span)) + } }; let red = (((v & 0xf000) >> 12) * 0x11) as u8; let green = (((v & 0x0f00) >> 8) * 0x11) as u8; @@ -98,7 +114,13 @@ fn parse_hex>( 6 => { let v = match u32::from_str_radix(&s, 16) { Ok(a) => a, - Err(_) => return Ok(Value::Ident(format!("#{}", s), QuoteKind::None).span(span)), + Err(_) => { + return Ok(Value::Ident( + InternedString::get_or_intern(format!("#{}", s)), + QuoteKind::None, + ) + .span(span)) + } }; let red = ((v & 0x00ff_0000) >> 16) as u8; let green = ((v & 0x0000_ff00) >> 8) as u8; @@ -111,7 +133,13 @@ fn parse_hex>( 8 => { let v = match u32::from_str_radix(&s, 16) { Ok(a) => a, - Err(_) => return Ok(Value::Ident(format!("#{}", s), QuoteKind::None).span(span)), + Err(_) => { + return Ok(Value::Ident( + InternedString::get_or_intern(format!("#{}", s)), + QuoteKind::None, + ) + .span(span)) + } }; let red = ((v & 0xff00_0000) >> 24) as u8; let green = ((v & 0x00ff_0000) >> 16) as u8; @@ -265,7 +293,10 @@ fn eat_op>( devour_whitespace(iter); space_separated.push(Spanned { node: Value::Ident( - format!("/{}", right.node.to_css_string(right.span)?), + InternedString::get_or_intern(format!( + "/{}", + right.node.to_css_string(right.span)? + )), QuoteKind::None, ), span: op.span.merge(right.span), @@ -309,7 +340,13 @@ fn eat_op>( devour_whitespace(iter); // special case when the value is literally "and" or "or" if iter.peek().is_none() { - space_separated.push(Value::Ident(op.to_string(), QuoteKind::None).span(op.span)); + space_separated.push( + Value::Ident( + InternedString::get_or_intern(op.to_string()), + QuoteKind::None, + ) + .span(op.span), + ); } else if let Some(left) = space_separated.pop() { devour_whitespace(iter); let right = single_value(iter, scope, super_selector, left.span)?; @@ -372,30 +409,23 @@ fn single_value>( let val = single_value(iter, scope, super_selector, span)?; Spanned { node: Value::Ident( - format!("/{}", val.node.to_css_string(val.span)?), + InternedString::get_or_intern(format!( + "/{}", + val.node.to_css_string(val.span)? + )), QuoteKind::None, ), span: next.span.merge(val.span), } } - Op::And => { - Spanned { - node: Value::Ident( - "and".into(), - QuoteKind::None, - ), - span: next.span, - } - } - Op::Or => { - Spanned { - node: Value::Ident( - "or".into(), - QuoteKind::None, - ), - span: next.span, - } - } + Op::And => Spanned { + node: Value::Ident(InternedString::get_or_intern("and"), QuoteKind::None), + span: next.span, + }, + Op::Or => Spanned { + node: Value::Ident(InternedString::get_or_intern("or"), QuoteKind::None), + span: next.span, + }, _ => { return Err(("Expected expression.", next.span).into()); } @@ -569,15 +599,17 @@ impl Value { ) -> SassResult> { let Spanned { node: mut s, span } = eat_ident(toks, scope, super_selector, span_before)?; - let lower = s.to_ascii_lowercase(); + let lower = InternedString::get_or_intern(s.to_ascii_lowercase()); - if lower == "progid" && toks.peek().is_some() && toks.peek().unwrap().kind == ':' { - s = lower; + if lower == *keywords::PROGID && toks.peek().is_some() && toks.peek().unwrap().kind == ':' { + s = "progid:".to_string(); toks.next(); - s.push(':'); s.push_str(&eat_progid(toks, scope, super_selector)?); return Ok(Spanned { - node: IntermediateValue::Value(Value::Ident(s, QuoteKind::None)), + node: IntermediateValue::Value(Value::Ident( + InternedString::get_or_intern(s), + QuoteKind::None, + )), span, }); } @@ -599,26 +631,34 @@ impl Value { .span(span)) } None => { - match lower.as_str() { - "calc" | "element" | "expression" => { - s = lower; + match lower { + _ if lower == *keywords::CALC + || lower == *keywords::ELEMENT + || lower == *keywords::EXPRESSION => + { + s = lower.resolve().to_string(); eat_calc_args(toks, scope, super_selector, &mut s)?; } // "min" => {} // "max" => {} - "url" => match try_eat_url(toks, scope, super_selector)? { - Some(val) => s = val, - None => s.push_str( - &eat_call_args(toks)?.to_css_string(scope, super_selector)?, - ), - }, + _ if lower == *keywords::URL => { + match try_eat_url(toks, scope, super_selector)? { + Some(val) => s = val, + None => s.push_str( + &eat_call_args(toks)? + .to_css_string(scope, super_selector)?, + ), + } + } _ => s.push_str( &eat_call_args(toks)?.to_css_string(scope, super_selector)?, ), } - return Ok( - IntermediateValue::Value(Value::Ident(s, QuoteKind::None)).span(span) - ); + return Ok(IntermediateValue::Value(Value::Ident( + InternedString::get_or_intern(s), + QuoteKind::None, + )) + .span(span)); } }, }; @@ -630,21 +670,24 @@ impl Value { .span(span)); } - if let Some(c) = NAMED_COLORS.get_by_name(&lower.as_str()) { + if let Some(c) = NAMED_COLORS.get_by_name(lower) { return Ok(IntermediateValue::Value(Value::Color(Box::new(Color::new( c[0], c[1], c[2], c[3], s, )))) .span(span)); } - Ok(match lower.as_str() { - "true" => IntermediateValue::Value(Value::True), - "false" => IntermediateValue::Value(Value::False), - "null" => IntermediateValue::Value(Value::Null), - "not" => IntermediateValue::Op(Op::Not), - "and" => IntermediateValue::Op(Op::And), - "or" => IntermediateValue::Op(Op::Or), - _ => IntermediateValue::Value(Value::Ident(s, QuoteKind::None)), + Ok(match lower { + _ if lower == *keywords::TRUE => IntermediateValue::Value(Value::True), + _ if lower == *keywords::FALSE => IntermediateValue::Value(Value::False), + _ if lower == *keywords::NULL => IntermediateValue::Value(Value::Null), + _ if lower == *keywords::NOT => IntermediateValue::Op(Op::Not), + _ if lower == *keywords::AND => IntermediateValue::Op(Op::And), + _ if lower == *keywords::OR => IntermediateValue::Op(Op::Or), + _ => IntermediateValue::Value(Value::Ident( + InternedString::get_or_intern(s), + QuoteKind::None, + )), } .span(span)) } diff --git a/src/value/sass_function.rs b/src/value/sass_function.rs index 18eb671..597348b 100644 --- a/src/value/sass_function.rs +++ b/src/value/sass_function.rs @@ -14,6 +14,7 @@ use std::fmt; use crate::args::CallArgs; use crate::atrule::Function; use crate::builtin::Builtin; +use crate::common::Identifier; use crate::error::SassResult; use crate::scope::Scope; use crate::selector::Selector; @@ -27,15 +28,15 @@ use crate::value::Value; /// for use in the builtin function `inspect()` #[derive(Clone)] pub(crate) enum SassFunction { - Builtin(Builtin, String), - UserDefined(Box, String), + Builtin(Builtin, Identifier), + UserDefined(Box, Identifier), } impl SassFunction { /// Get the name of the function referenced /// /// Used mainly in debugging and `inspect()` - pub fn name(&self) -> &str { + pub fn name(&self) -> &Identifier { match self { Self::Builtin(_, name) => name, Self::UserDefined(_, name) => name,