diff --git a/Cargo.toml b/Cargo.toml index 5fc5e54..80fe9d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,6 @@ codemap = "0.1.3" peekmore = "0.5.2" wasm-bindgen = { version = "0.2.63", optional = true } beef = "0.4.4" -bitflags = "1.2.1" # criterion is not a dev-dependency because it makes tests take too # long to compile, and you cannot make dev-dependencies optional criterion = { version = "0.3.2", optional = true } diff --git a/src/builtin/meta.rs b/src/builtin/meta.rs index c809781..b29e783 100644 --- a/src/builtin/meta.rs +++ b/src/builtin/meta.rs @@ -6,7 +6,7 @@ use crate::{ args::CallArgs, common::QuoteKind, error::SassResult, - parse::{Flags, Parser}, + parse::Parser, unit::Unit, value::{SassFunction, Value}, }; @@ -232,7 +232,7 @@ fn call(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { #[allow(clippy::needless_pass_by_value)] fn content_exists(args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(0)?; - if !parser.flags.contains(Flags::IN_MIXIN) { + if !parser.flags.in_mixin() { return Err(( "content-exists() may only be called within a mixin.", parser.span_before, diff --git a/src/lib.rs b/src/lib.rs index 8b6ea86..ab490bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,7 +94,10 @@ pub(crate) use crate::token::Token; use crate::{ lexer::Lexer, output::Css, - parse::{common::NeverEmptyVec, Flags, Parser}, + parse::{ + common::{ContextFlags, NeverEmptyVec}, + Parser, + }, scope::Scope, selector::{Extender, Selector}, }; @@ -149,7 +152,7 @@ pub fn from_path(p: &str) -> Result { super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)), span_before: empty_span, content: &mut Vec::new(), - flags: Flags::empty(), + flags: ContextFlags::empty(), at_root: true, at_root_has_selector: false, extender: &mut Extender::new(empty_span), @@ -191,7 +194,7 @@ pub fn from_string(p: String) -> Result { super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)), span_before: empty_span, content: &mut Vec::new(), - flags: Flags::empty(), + flags: ContextFlags::empty(), at_root: true, at_root_has_selector: false, extender: &mut Extender::new(empty_span), @@ -224,7 +227,7 @@ pub fn from_string(p: String) -> std::result::Result { super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)), span_before: empty_span, content: &mut Vec::new(), - flags: Flags::empty(), + flags: ContextFlags::empty(), at_root: true, at_root_has_selector: false, extender: &mut Extender::new(empty_span), diff --git a/src/parse/common.rs b/src/parse/common.rs index 03f3cdd..11d31a3 100644 --- a/src/parse/common.rs +++ b/src/parse/common.rs @@ -1,4 +1,7 @@ -use std::slice::IterMut; +use std::{ + ops::{BitAnd, BitOr}, + slice::IterMut, +}; use codemap::Spanned; @@ -75,3 +78,50 @@ impl Branch { Branch { cond, toks } } } + +#[derive(Debug, Copy, Clone)] +pub(crate) struct ContextFlags(u8); + +pub(crate) struct ContextFlag(u8); + +impl ContextFlags { + pub const IN_MIXIN: ContextFlag = ContextFlag(1); + pub const IN_FUNCTION: ContextFlag = ContextFlag(1 << 1); + pub const IN_CONTROL_FLOW: ContextFlag = ContextFlag(1 << 2); + pub const IN_KEYFRAMES: ContextFlag = ContextFlag(1 << 3); + + pub const fn empty() -> Self { + Self(0) + } + + pub fn in_mixin(self) -> bool { + (self.0 & Self::IN_MIXIN) != 0 + } + + pub fn in_function(self) -> bool { + (self.0 & Self::IN_FUNCTION) != 0 + } + + pub fn in_control_flow(self) -> bool { + (self.0 & Self::IN_CONTROL_FLOW) != 0 + } + + pub fn in_keyframes(self) -> bool { + (self.0 & Self::IN_KEYFRAMES) != 0 + } +} + +impl BitAnd for u8 { + type Output = Self; + #[inline] + fn bitand(self, rhs: ContextFlag) -> Self::Output { + self & rhs.0 + } +} + +impl BitOr for ContextFlags { + type Output = Self; + fn bitor(self, rhs: ContextFlag) -> Self::Output { + Self(self.0 | rhs.0) + } +} diff --git a/src/parse/function.rs b/src/parse/function.rs index 5a280c8..0b56d99 100644 --- a/src/parse/function.rs +++ b/src/parse/function.rs @@ -11,7 +11,7 @@ use crate::{ Token, }; -use super::{Flags, NeverEmptyVec, Parser, Stmt}; +use super::{common::ContextFlags, NeverEmptyVec, Parser, Stmt}; /// Names that functions are not allowed to have const FORBIDDEN_IDENTIFIERS: [&str; 7] = @@ -22,11 +22,11 @@ impl<'a> Parser<'a> { self.whitespace_or_comment(); let Spanned { node: name, span } = self.parse_identifier()?; - if self.flags.contains(Flags::IN_MIXIN) { + if self.flags.in_mixin() { return Err(("Mixins may not contain function declarations.", span).into()); } - if self.flags.contains(Flags::IN_CONTROL_FLOW) { + if self.flags.in_control_flow() { return Err(("Functions may not be declared in control directives.", span).into()); } @@ -88,7 +88,7 @@ impl<'a> Parser<'a> { super_selectors: self.super_selectors, span_before: self.span_before, content: self.content, - flags: self.flags | Flags::IN_FUNCTION, + flags: self.flags | ContextFlags::IN_FUNCTION, at_root: false, at_root_has_selector: self.at_root_has_selector, extender: self.extender, diff --git a/src/parse/keyframes.rs b/src/parse/keyframes.rs index a083b71..b5c15af 100644 --- a/src/parse/keyframes.rs +++ b/src/parse/keyframes.rs @@ -10,7 +10,7 @@ use crate::{ Token, }; -use super::{Flags, Parser}; +use super::{common::ContextFlags, Parser}; impl fmt::Display for KeyframesSelector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -189,7 +189,7 @@ impl<'a> Parser<'a> { super_selectors: self.super_selectors, span_before: self.span_before, content: self.content, - flags: self.flags | Flags::IN_KEYFRAMES, + flags: self.flags | ContextFlags::IN_KEYFRAMES, at_root: false, at_root_has_selector: self.at_root_has_selector, extender: self.extender, diff --git a/src/parse/mixin.rs b/src/parse/mixin.rs index 5083d68..48bb4b0 100644 --- a/src/parse/mixin.rs +++ b/src/parse/mixin.rs @@ -10,7 +10,7 @@ use crate::{ Token, }; -use super::{Flags, NeverEmptyVec, Parser, Stmt}; +use super::{common::ContextFlags, NeverEmptyVec, Parser, Stmt}; impl<'a> Parser<'a> { pub(super) fn parse_mixin(&mut self) -> SassResult<()> { @@ -127,7 +127,7 @@ impl<'a> Parser<'a> { global_scope: self.global_scope, super_selectors: self.super_selectors, span_before: self.span_before, - flags: self.flags | Flags::IN_MIXIN, + flags: self.flags | ContextFlags::IN_MIXIN, content: self.content, at_root: false, at_root_has_selector: self.at_root_has_selector, @@ -141,7 +141,7 @@ impl<'a> Parser<'a> { } pub(super) fn parse_content_rule(&mut self) -> SassResult> { - if self.flags.contains(Flags::IN_MIXIN) { + if self.flags.in_mixin() { let mut scope = self .content .last() diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 5811165..504161d 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -26,7 +26,7 @@ use crate::{ {Cow, Token}, }; -use common::{Branch, NeverEmptyVec, SelectorOrStyle}; +use common::{Branch, ContextFlags, NeverEmptyVec, SelectorOrStyle}; pub(crate) use value::{HigherIntermediateValue, ValueVisitor}; @@ -66,17 +66,6 @@ pub(crate) enum Stmt { KeyframesRuleSet(Box), } -bitflags::bitflags! { - // todo: try to remove the flag IN_CONTROL_FLOW - /// Flags to indicate the context during parsing. - pub struct Flags: u8 { - const IN_MIXIN = 1; - const IN_FUNCTION = 1 << 1; - const IN_CONTROL_FLOW = 1 << 2; - const IN_KEYFRAMES = 1 << 3; - } -} - /// We could use a generic for the toks, but it makes the API /// much simpler to work with if it isn't generic. The performance /// hit (if there is one) is not important for now. @@ -90,7 +79,7 @@ pub(crate) struct Parser<'a> { pub super_selectors: &'a mut NeverEmptyVec, pub span_before: Span, pub content: &'a mut Vec, - pub flags: Flags, + pub flags: ContextFlags, /// Whether this parser is at the root of the document /// E.g. not inside a style, mixin, or function pub at_root: bool, @@ -105,7 +94,7 @@ impl<'a> Parser<'a> { let mut stmts = Vec::new(); while self.toks.peek().is_some() { stmts.append(&mut self.parse_stmt()?); - if self.flags.contains(Flags::IN_FUNCTION) && !stmts.is_empty() { + if self.flags.in_function() && !stmts.is_empty() { return Ok(stmts); } self.at_root = true; @@ -116,7 +105,7 @@ impl<'a> Parser<'a> { fn parse_stmt(&mut self) -> SassResult> { let mut stmts = Vec::new(); while let Some(Token { kind, pos }) = self.toks.peek() { - if self.flags.contains(Flags::IN_FUNCTION) && !stmts.is_empty() { + if self.flags.in_function() && !stmts.is_empty() { return Ok(stmts); } self.span_before = *pos; @@ -132,7 +121,7 @@ impl<'a> Parser<'a> { AtRuleKind::Include => stmts.append(&mut self.parse_include()?), AtRuleKind::Function => self.parse_function()?, AtRuleKind::Return => { - if self.flags.contains(Flags::IN_FUNCTION) { + if self.flags.in_function() { return Ok(vec![Stmt::Return(self.parse_return()?)]); } else { return Err(( @@ -242,7 +231,7 @@ impl<'a> Parser<'a> { // dart-sass seems to special-case the error message here? '!' | '{' => return Err(("expected \"}\".", *pos).into()), _ => { - if self.flags.contains(Flags::IN_KEYFRAMES) { + if self.flags.in_keyframes() { match self.is_selector_or_style()? { SelectorOrStyle::Style(property, value) => { if let Some(value) = value { @@ -611,7 +600,7 @@ impl<'a> Parser<'a> { super_selectors: self.super_selectors, span_before: self.span_before, content: self.content, - flags: self.flags | Flags::IN_CONTROL_FLOW, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, @@ -631,7 +620,7 @@ impl<'a> Parser<'a> { super_selectors: self.super_selectors, span_before: self.span_before, content: self.content, - flags: self.flags | Flags::IN_CONTROL_FLOW, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, @@ -762,7 +751,7 @@ impl<'a> Parser<'a> { span: var.span, }, ); - if self.flags.contains(Flags::IN_FUNCTION) { + if self.flags.in_function() { let these_stmts = Parser { toks: &mut body.clone().into_iter().peekmore(), map: self.map, @@ -772,7 +761,7 @@ impl<'a> Parser<'a> { super_selectors: self.super_selectors, span_before: self.span_before, content: self.content, - flags: self.flags | Flags::IN_CONTROL_FLOW, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, @@ -792,7 +781,7 @@ impl<'a> Parser<'a> { super_selectors: self.super_selectors, span_before: self.span_before, content: self.content, - flags: self.flags | Flags::IN_CONTROL_FLOW, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, @@ -830,7 +819,7 @@ impl<'a> Parser<'a> { let mut val = self.parse_value_from_vec(cond.clone())?; self.scopes.push(self.scopes.last().clone()); while val.node.is_true() { - if self.flags.contains(Flags::IN_FUNCTION) { + if self.flags.in_function() { let these_stmts = Parser { toks: &mut body.clone().into_iter().peekmore(), map: self.map, @@ -840,7 +829,7 @@ impl<'a> Parser<'a> { super_selectors: self.super_selectors, span_before: self.span_before, content: self.content, - flags: self.flags | Flags::IN_CONTROL_FLOW, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, @@ -860,7 +849,7 @@ impl<'a> Parser<'a> { super_selectors: self.super_selectors, span_before: self.span_before, content: self.content, - flags: self.flags | Flags::IN_CONTROL_FLOW, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, @@ -958,7 +947,7 @@ impl<'a> Parser<'a> { } } - if self.flags.contains(Flags::IN_FUNCTION) { + if self.flags.in_function() { let these_stmts = Parser { toks: &mut body.clone().into_iter().peekmore(), map: self.map, @@ -968,7 +957,7 @@ impl<'a> Parser<'a> { super_selectors: self.super_selectors, span_before: self.span_before, content: self.content, - flags: self.flags | Flags::IN_CONTROL_FLOW, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, @@ -988,7 +977,7 @@ impl<'a> Parser<'a> { super_selectors: self.super_selectors, span_before: self.span_before, content: self.content, - flags: self.flags | Flags::IN_CONTROL_FLOW, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, at_root: self.at_root, at_root_has_selector: self.at_root_has_selector, extender: self.extender, diff --git a/src/parse/variable.rs b/src/parse/variable.rs index 146d074..5143265 100644 --- a/src/parse/variable.rs +++ b/src/parse/variable.rs @@ -11,7 +11,7 @@ use crate::{ Token, }; -use super::{Flags, Parser}; +use super::Parser; #[derive(Debug)] struct VariableValue { @@ -46,7 +46,7 @@ impl<'a> Parser<'a> { } if value.default { - if self.at_root && !self.flags.contains(Flags::IN_CONTROL_FLOW) { + if self.at_root && !self.flags.in_control_flow() { if !self.global_scope.var_exists_no_global(&ident) { self.global_scope.insert_var(ident, value.value); } @@ -60,7 +60,7 @@ impl<'a> Parser<'a> { } } } else if self.at_root { - if self.flags.contains(Flags::IN_CONTROL_FLOW) { + if self.flags.in_control_flow() { if self.global_scope.var_exists_no_global(&ident) { self.global_scope.insert_var(ident, value.value); } else {