implement string interning
This commit is contained in:
parent
5551a8f8a8
commit
2dfda192bc
@ -64,6 +64,7 @@ beef = "0.4.4"
|
|||||||
# long to compile, and you cannot make dev-dependencies optional
|
# long to compile, and you cannot make dev-dependencies optional
|
||||||
criterion = { version = "0.3.2", optional = true }
|
criterion = { version = "0.3.2", optional = true }
|
||||||
indexmap = "1.4.0"
|
indexmap = "1.4.0"
|
||||||
|
lasso = "0.2.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["commandline", "random"]
|
default = ["commandline", "random"]
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::fmt::{self, Display, Write};
|
use std::fmt::{self, Display, Write};
|
||||||
|
|
||||||
|
use crate::interner::InternedString;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Op {
|
pub enum Op {
|
||||||
Equal,
|
Equal,
|
||||||
@ -111,27 +113,35 @@ impl ListSeparator {
|
|||||||
/// This struct protects that invariant by normalizing all
|
/// This struct protects that invariant by normalizing all
|
||||||
/// underscores into hypens.
|
/// underscores into hypens.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
||||||
pub(crate) struct Identifier(String);
|
pub(crate) struct Identifier(InternedString);
|
||||||
|
|
||||||
impl From<String> for Identifier {
|
impl From<String> for Identifier {
|
||||||
fn from(s: String) -> Identifier {
|
fn from(s: String) -> Identifier {
|
||||||
if s.contains('_') {
|
Identifier(InternedString::get_or_intern(if s.contains('_') {
|
||||||
Identifier(s.replace('_', "-"))
|
s.replace('_', "-")
|
||||||
} else {
|
} else {
|
||||||
Identifier(s)
|
s
|
||||||
}
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&String> for Identifier {
|
impl From<&String> for Identifier {
|
||||||
fn from(s: &String) -> Identifier {
|
fn from(s: &String) -> Identifier {
|
||||||
Identifier(s.replace('_', "-"))
|
if s.contains('_') {
|
||||||
|
Identifier(InternedString::get_or_intern(s.replace('_', "-")))
|
||||||
|
} else {
|
||||||
|
Identifier(InternedString::get_or_intern(s))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for Identifier {
|
impl From<&str> for Identifier {
|
||||||
fn from(s: &str) -> Identifier {
|
fn from(s: &str) -> Identifier {
|
||||||
Identifier(s.replace('_', "-"))
|
if s.contains('_') {
|
||||||
|
Identifier(InternedString::get_or_intern(s.replace('_', "-")))
|
||||||
|
} else {
|
||||||
|
Identifier(InternedString::get_or_intern(s))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,15 +151,9 @@ impl Display for Identifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Identifier {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(String::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Identifier {
|
impl Identifier {
|
||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
&self.0
|
self.0.resolve_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
37
src/interner.rs
Normal file
37
src/interner.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use lasso::{Rodeo, Spur};
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::fmt::{self, Display};
|
||||||
|
|
||||||
|
thread_local!(static STRINGS: RefCell<Rodeo<Spur>> = RefCell::new(Rodeo::default()));
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
pub(crate) struct InternedString(Spur);
|
||||||
|
|
||||||
|
impl InternedString {
|
||||||
|
pub fn get_or_intern<T: AsRef<str>>(s: T) -> Self {
|
||||||
|
Self(STRINGS.with(|interner| interner.borrow_mut().get_or_intern(s)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn resolve(self) -> String {
|
||||||
|
STRINGS.with(|interner| interner.borrow().resolve(&self.0).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn is_empty(self) -> bool {
|
||||||
|
self.resolve_ref() == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_ref<'a>(self) -> &'a str {
|
||||||
|
unsafe {
|
||||||
|
STRINGS.with(|interner| &(*(interner.as_ptr()).as_ref().unwrap().resolve(&self.0)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for InternedString {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
STRINGS.with(|interner| write!(f, "{}", interner.borrow().resolve(&self.0)))
|
||||||
|
}
|
||||||
|
}
|
@ -116,6 +116,7 @@ mod selector;
|
|||||||
mod style;
|
mod style;
|
||||||
mod token;
|
mod token;
|
||||||
mod unit;
|
mod unit;
|
||||||
|
mod interner;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
|
@ -351,18 +351,14 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(vals)
|
Ok(vals)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn eval_args(
|
pub(super) fn eval_args(&mut self, fn_args: FuncArgs, mut args: CallArgs) -> SassResult<Scope> {
|
||||||
&mut self,
|
|
||||||
mut fn_args: FuncArgs,
|
|
||||||
mut args: CallArgs,
|
|
||||||
) -> SassResult<Scope> {
|
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
if fn_args.0.is_empty() {
|
if fn_args.0.is_empty() {
|
||||||
args.max_args(0)?;
|
args.max_args(0)?;
|
||||||
return Ok(scope);
|
return Ok(scope);
|
||||||
}
|
}
|
||||||
self.scopes.enter_new_scope();
|
self.scopes.enter_new_scope();
|
||||||
for (idx, arg) in fn_args.0.iter_mut().enumerate() {
|
for (idx, mut arg) in fn_args.0.into_iter().enumerate() {
|
||||||
if arg.is_variadic {
|
if arg.is_variadic {
|
||||||
let span = args.span();
|
let span = args.span();
|
||||||
let arg_list = Value::ArgList(self.variadic_args(args)?);
|
let arg_list = Value::ArgList(self.variadic_args(args)?);
|
||||||
@ -387,7 +383,7 @@ impl<'a> Parser<'a> {
|
|||||||
},
|
},
|
||||||
}?;
|
}?;
|
||||||
self.scopes.insert_var(arg.name.clone(), val.clone());
|
self.scopes.insert_var(arg.name.clone(), val.clone());
|
||||||
scope.insert_var(mem::take(&mut arg.name), val);
|
scope.insert_var(arg.name, val);
|
||||||
}
|
}
|
||||||
self.scopes.exit_scope();
|
self.scopes.exit_scope();
|
||||||
Ok(scope)
|
Ok(scope)
|
||||||
|
@ -2,7 +2,7 @@ use std::ops::{BitAnd, BitOr};
|
|||||||
|
|
||||||
use codemap::Spanned;
|
use codemap::Spanned;
|
||||||
|
|
||||||
use crate::value::Value;
|
use crate::{interner::InternedString, value::Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct NeverEmptyVec<T> {
|
pub(crate) struct NeverEmptyVec<T> {
|
||||||
@ -41,7 +41,7 @@ impl<T> NeverEmptyVec<T> {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) enum SelectorOrStyle {
|
pub(super) enum SelectorOrStyle {
|
||||||
Selector(String),
|
Selector(String),
|
||||||
Style(String, Option<Box<Spanned<Value>>>),
|
Style(InternedString, Option<Box<Spanned<Value>>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -2,6 +2,7 @@ use codemap::Spanned;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::SassResult,
|
error::SassResult,
|
||||||
|
interner::InternedString,
|
||||||
style::Style,
|
style::Style,
|
||||||
utils::{is_name, is_name_start},
|
utils::{is_name, is_name_start},
|
||||||
value::Value,
|
value::Value,
|
||||||
@ -126,7 +127,10 @@ impl<'a> Parser<'a> {
|
|||||||
let len = toks.len();
|
let len = toks.len();
|
||||||
if let Ok(val) = self.parse_value_from_vec(toks) {
|
if let Ok(val) = self.parse_value_from_vec(toks) {
|
||||||
self.toks.take(len).for_each(drop);
|
self.toks.take(len).for_each(drop);
|
||||||
return Ok(SelectorOrStyle::Style(property, Some(Box::new(val))));
|
return Ok(SelectorOrStyle::Style(
|
||||||
|
InternedString::get_or_intern(property),
|
||||||
|
Some(Box::new(val)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +140,7 @@ impl<'a> Parser<'a> {
|
|||||||
property.push(':');
|
property.push(':');
|
||||||
return Ok(SelectorOrStyle::Selector(property));
|
return Ok(SelectorOrStyle::Selector(property));
|
||||||
}
|
}
|
||||||
_ => SelectorOrStyle::Style(property, None),
|
_ => SelectorOrStyle::Style(InternedString::get_or_intern(property), None),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -172,7 +176,10 @@ impl<'a> Parser<'a> {
|
|||||||
self.parse_value()
|
self.parse_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn parse_style_group(&mut self, super_property: String) -> SassResult<Vec<Style>> {
|
pub(super) fn parse_style_group(
|
||||||
|
&mut self,
|
||||||
|
super_property: InternedString,
|
||||||
|
) -> SassResult<Vec<Style>> {
|
||||||
let mut styles = Vec::new();
|
let mut styles = Vec::new();
|
||||||
self.whitespace();
|
self.whitespace();
|
||||||
while let Some(tok) = self.toks.peek().cloned() {
|
while let Some(tok) = self.toks.peek().cloned() {
|
||||||
@ -181,7 +188,9 @@ impl<'a> Parser<'a> {
|
|||||||
self.toks.next();
|
self.toks.next();
|
||||||
self.whitespace();
|
self.whitespace();
|
||||||
loop {
|
loop {
|
||||||
let property = self.parse_property(super_property.clone())?;
|
let property = InternedString::get_or_intern(
|
||||||
|
self.parse_property(super_property.resolve())?,
|
||||||
|
);
|
||||||
if let Some(tok) = self.toks.peek() {
|
if let Some(tok) = self.toks.peek() {
|
||||||
if tok.kind == '{' {
|
if tok.kind == '{' {
|
||||||
styles.append(&mut self.parse_style_group(property)?);
|
styles.append(&mut self.parse_style_group(property)?);
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use codemap::Spanned;
|
use codemap::Spanned;
|
||||||
|
|
||||||
use crate::{error::SassResult, value::Value};
|
use crate::{error::SassResult, interner::InternedString, value::Value};
|
||||||
|
|
||||||
/// A style: `color: red`
|
/// A style: `color: red`
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub(crate) struct Style {
|
pub(crate) struct Style {
|
||||||
pub property: String,
|
pub property: InternedString,
|
||||||
pub value: Box<Spanned<Value>>,
|
pub value: Box<Spanned<Value>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user