This commit is contained in:
Connor Skees 2020-07-05 09:36:04 -04:00
commit eec454c2d0
12 changed files with 117 additions and 201 deletions

View File

@ -60,6 +60,7 @@ codemap = "0.1.3"
peekmore = "0.5.2" peekmore = "0.5.2"
wasm-bindgen = { version = "0.2.63", optional = true } wasm-bindgen = { version = "0.2.63", optional = true }
beef = "0.4.4" beef = "0.4.4"
bitflags = "1.2.1"
# criterion is not a dev-dependency because it makes tests take too # criterion is not a dev-dependency because it makes tests take too
# 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 }

View File

@ -6,7 +6,7 @@ use crate::{
args::CallArgs, args::CallArgs,
common::QuoteKind, common::QuoteKind,
error::SassResult, error::SassResult,
parse::Parser, parse::{Flags, Parser},
unit::Unit, unit::Unit,
value::{SassFunction, Value}, value::{SassFunction, Value},
}; };
@ -232,7 +232,7 @@ fn call(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
fn content_exists(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { fn content_exists(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(0)?; args.max_args(0)?;
if !parser.in_mixin { if !parser.flags.contains(Flags::IN_MIXIN) {
return Err(( return Err((
"content-exists() may only be called within a mixin.", "content-exists() may only be called within a mixin.",
parser.span_before, parser.span_before,

View File

@ -94,7 +94,7 @@ pub(crate) use crate::token::Token;
use crate::{ use crate::{
lexer::Lexer, lexer::Lexer,
output::Css, output::Css,
parse::{common::NeverEmptyVec, Parser}, parse::{common::NeverEmptyVec, Flags, Parser},
scope::Scope, scope::Scope,
selector::{Extender, Selector}, selector::{Extender, Selector},
}; };
@ -149,13 +149,10 @@ pub fn from_path(p: &str) -> Result<String> {
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)), super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
span_before: empty_span, span_before: empty_span,
content: &mut Vec::new(), content: &mut Vec::new(),
in_mixin: false, flags: Flags::empty(),
in_function: false,
in_control_flow: false,
at_root: true, at_root: true,
at_root_has_selector: false, at_root_has_selector: false,
extender: &mut Extender::new(empty_span), extender: &mut Extender::new(empty_span),
in_keyframes: false,
} }
.parse() .parse()
.map_err(|e| raw_to_parse_error(&map, *e))?; .map_err(|e| raw_to_parse_error(&map, *e))?;
@ -194,13 +191,10 @@ pub fn from_string(p: String) -> Result<String> {
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)), super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
span_before: empty_span, span_before: empty_span,
content: &mut Vec::new(), content: &mut Vec::new(),
in_mixin: false, flags: Flags::empty(),
in_function: false,
in_control_flow: false,
at_root: true, at_root: true,
at_root_has_selector: false, at_root_has_selector: false,
extender: &mut Extender::new(empty_span), extender: &mut Extender::new(empty_span),
in_keyframes: false,
} }
.parse() .parse()
.map_err(|e| raw_to_parse_error(&map, *e))?; .map_err(|e| raw_to_parse_error(&map, *e))?;
@ -230,13 +224,10 @@ pub fn from_string(p: String) -> std::result::Result<String, JsValue> {
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)), super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
span_before: empty_span, span_before: empty_span,
content: &mut Vec::new(), content: &mut Vec::new(),
in_mixin: false, flags: Flags::empty(),
in_function: false,
in_control_flow: false,
at_root: true, at_root: true,
at_root_has_selector: false, at_root_has_selector: false,
extender: &mut Extender::new(empty_span), extender: &mut Extender::new(empty_span),
in_keyframes: false,
} }
.parse() .parse()
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?; .map_err(|e| raw_to_parse_error(&map, *e).to_string())?;

View File

@ -11,7 +11,7 @@ use crate::{
Token, Token,
}; };
use super::{NeverEmptyVec, Parser, Stmt}; use super::{Flags, NeverEmptyVec, Parser, Stmt};
/// Names that functions are not allowed to have /// Names that functions are not allowed to have
const FORBIDDEN_IDENTIFIERS: [&str; 7] = const FORBIDDEN_IDENTIFIERS: [&str; 7] =
@ -22,11 +22,11 @@ impl<'a> Parser<'a> {
self.whitespace_or_comment(); self.whitespace_or_comment();
let Spanned { node: name, span } = self.parse_identifier()?; let Spanned { node: name, span } = self.parse_identifier()?;
if self.in_mixin { if self.flags.contains(Flags::IN_MIXIN) {
return Err(("Mixins may not contain function declarations.", span).into()); return Err(("Mixins may not contain function declarations.", span).into());
} }
if self.in_control_flow { if self.flags.contains(Flags::IN_CONTROL_FLOW) {
return Err(("Functions may not be declared in control directives.", span).into()); return Err(("Functions may not be declared in control directives.", span).into());
} }
@ -88,13 +88,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags | Flags::IN_FUNCTION,
in_function: true,
in_control_flow: self.in_control_flow,
at_root: false, at_root: false,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse()?; .parse()?;

View File

@ -83,13 +83,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: file.span.subspan(0, 0), span_before: file.span.subspan(0, 0),
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags,
in_function: self.in_function,
in_control_flow: self.in_control_flow,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse(); .parse();
} }

View File

@ -10,7 +10,7 @@ use crate::{
Token, Token,
}; };
use super::Parser; use super::{Flags, Parser};
impl fmt::Display for KeyframesSelector { impl fmt::Display for KeyframesSelector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -55,8 +55,7 @@ impl<'a, 'b> KeyframesSelectorParser<'a, 'b> {
} }
} }
'0'..='9' => { '0'..='9' => {
let mut num = String::new(); let num = eat_whole_number(self.parser.toks);
eat_whole_number(self.parser.toks, &mut num);
if !matches!(self.parser.toks.next(), Some(Token { kind: '%', .. })) { if !matches!(self.parser.toks.next(), Some(Token { kind: '%', .. })) {
return Err(("expected \"%\".", tok.pos).into()); return Err(("expected \"%\".", tok.pos).into());
} }
@ -81,7 +80,6 @@ impl<'a, 'b> KeyframesSelectorParser<'a, 'b> {
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
fn parse_keyframes_name(&mut self) -> SassResult<String> { fn parse_keyframes_name(&mut self) -> SassResult<String> {
let mut name = String::new(); let mut name = String::new();
let mut found_open_brace = false;
self.whitespace_or_comment(); self.whitespace_or_comment();
while let Some(tok) = self.toks.next() { while let Some(tok) = self.toks.next() {
match tok.kind { match tok.kind {
@ -98,19 +96,14 @@ impl<'a> Parser<'a> {
name.push(' '); name.push(' ');
} }
'{' => { '{' => {
found_open_brace = true; // todo: we can avoid the reallocation by trimming before emitting
break; // (in `output.rs`)
return Ok(name.trim().to_string());
} }
_ => name.push(tok.kind), _ => name.push(tok.kind),
} }
} }
Err(("expected \"{\".", self.span_before).into())
if !found_open_brace {
return Err(("expected \"{\".", self.span_before).into());
}
// todo: we can avoid the reallocation by trimming before emitting (in `output.rs`)
Ok(name.trim().to_string())
} }
pub(super) fn parse_keyframes_selector( pub(super) fn parse_keyframes_selector(
@ -125,8 +118,6 @@ impl<'a> Parser<'a> {
self.span_before = span; self.span_before = span;
let mut found_curly = false;
while let Some(tok) = self.toks.next() { while let Some(tok) = self.toks.next() {
span = span.merge(tok.pos()); span = span.merge(tok.pos());
match tok.kind { match tok.kind {
@ -157,23 +148,11 @@ impl<'a> Parser<'a> {
string.push(' '); string.push(' ');
} }
'{' => { '{' => {
found_curly = true; let sel_toks: Vec<Token> =
break; string.chars().map(|x| Token::new(span, x)).collect();
}
c => string.push(c),
}
}
if !found_curly {
return Err(("expected \"{\".", span).into());
}
let sel_toks: Vec<Token> = string.chars().map(|x| Token::new(span, x)).collect();
let mut iter = sel_toks.into_iter().peekmore();
let selector = KeyframesSelectorParser::new(&mut Parser { let selector = KeyframesSelectorParser::new(&mut Parser {
toks: &mut iter, toks: &mut sel_toks.into_iter().peekmore(),
map: self.map, map: self.map,
path: self.path, path: self.path,
scopes: self.scopes, scopes: self.scopes,
@ -181,17 +160,19 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags,
in_function: self.in_function,
in_control_flow: self.in_control_flow,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
}) })
.parse_keyframes_selector()?; .parse_keyframes_selector()?;
Ok(selector) return Ok(selector);
}
c => string.push(c),
}
}
Err(("expected \"{\".", span).into())
} }
pub(super) fn parse_keyframes(&mut self) -> SassResult<Stmt> { pub(super) fn parse_keyframes(&mut self) -> SassResult<Stmt> {
@ -208,11 +189,8 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags | Flags::IN_KEYFRAMES,
in_function: self.in_function,
in_control_flow: self.in_control_flow,
at_root: false, at_root: false,
in_keyframes: true,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
} }

View File

@ -10,7 +10,7 @@ use crate::{
Token, Token,
}; };
use super::{NeverEmptyVec, Parser, Stmt}; use super::{Flags, NeverEmptyVec, Parser, Stmt};
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
pub(super) fn parse_mixin(&mut self) -> SassResult<()> { pub(super) fn parse_mixin(&mut self) -> SassResult<()> {
@ -127,14 +127,11 @@ impl<'a> Parser<'a> {
global_scope: self.global_scope, global_scope: self.global_scope,
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
in_mixin: true, flags: self.flags | Flags::IN_MIXIN,
in_function: self.in_function,
in_control_flow: self.in_control_flow,
content: self.content, content: self.content,
at_root: false, at_root: false,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse()?; .parse()?;
@ -144,7 +141,7 @@ impl<'a> Parser<'a> {
} }
pub(super) fn parse_content_rule(&mut self) -> SassResult<Vec<Stmt>> { pub(super) fn parse_content_rule(&mut self) -> SassResult<Vec<Stmt>> {
if self.in_mixin { if self.flags.contains(Flags::IN_MIXIN) {
let mut scope = self let mut scope = self
.content .content
.last() .last()
@ -175,14 +172,11 @@ impl<'a> Parser<'a> {
global_scope: self.global_scope, global_scope: self.global_scope,
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
in_mixin: self.in_mixin, flags: self.flags,
in_function: self.in_function,
in_control_flow: self.in_control_flow,
content: self.content, content: self.content,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse()? .parse()?
} else { } else {

View File

@ -66,11 +66,21 @@ pub(crate) enum Stmt {
KeyframesRuleSet(Box<KeyframesRuleSet>), KeyframesRuleSet(Box<KeyframesRuleSet>),
} }
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 /// 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 /// much simpler to work with if it isn't generic. The performance
/// hit (if there is one) is not important for now. /// hit (if there is one) is not important for now.
// todo: refactor `in_mixin`, in_function`, and `at_root` into state machine enum // todo: merge at_root and at_root_has_selector into an enum
#[allow(clippy::struct_excessive_bools)]
pub(crate) struct Parser<'a> { pub(crate) struct Parser<'a> {
pub toks: &'a mut PeekMoreIterator<IntoIter<Token>>, pub toks: &'a mut PeekMoreIterator<IntoIter<Token>>,
pub map: &'a mut CodeMap, pub map: &'a mut CodeMap,
@ -80,10 +90,7 @@ pub(crate) struct Parser<'a> {
pub super_selectors: &'a mut NeverEmptyVec<Selector>, pub super_selectors: &'a mut NeverEmptyVec<Selector>,
pub span_before: Span, pub span_before: Span,
pub content: &'a mut Vec<Content>, pub content: &'a mut Vec<Content>,
pub in_mixin: bool, pub flags: Flags,
pub in_function: bool,
pub in_control_flow: bool,
pub in_keyframes: bool,
/// Whether this parser is at the root of the document /// Whether this parser is at the root of the document
/// E.g. not inside a style, mixin, or function /// E.g. not inside a style, mixin, or function
pub at_root: bool, pub at_root: bool,
@ -98,7 +105,7 @@ impl<'a> Parser<'a> {
let mut stmts = Vec::new(); let mut stmts = Vec::new();
while self.toks.peek().is_some() { while self.toks.peek().is_some() {
stmts.append(&mut self.parse_stmt()?); stmts.append(&mut self.parse_stmt()?);
if self.in_function && !stmts.is_empty() { if self.flags.contains(Flags::IN_FUNCTION) && !stmts.is_empty() {
return Ok(stmts); return Ok(stmts);
} }
self.at_root = true; self.at_root = true;
@ -109,7 +116,7 @@ impl<'a> Parser<'a> {
fn parse_stmt(&mut self) -> SassResult<Vec<Stmt>> { fn parse_stmt(&mut self) -> SassResult<Vec<Stmt>> {
let mut stmts = Vec::new(); let mut stmts = Vec::new();
while let Some(Token { kind, pos }) = self.toks.peek() { while let Some(Token { kind, pos }) = self.toks.peek() {
if self.in_function && !stmts.is_empty() { if self.flags.contains(Flags::IN_FUNCTION) && !stmts.is_empty() {
return Ok(stmts); return Ok(stmts);
} }
self.span_before = *pos; self.span_before = *pos;
@ -125,7 +132,7 @@ impl<'a> Parser<'a> {
AtRuleKind::Include => stmts.append(&mut self.parse_include()?), AtRuleKind::Include => stmts.append(&mut self.parse_include()?),
AtRuleKind::Function => self.parse_function()?, AtRuleKind::Function => self.parse_function()?,
AtRuleKind::Return => { AtRuleKind::Return => {
if self.in_function { if self.flags.contains(Flags::IN_FUNCTION) {
return Ok(vec![Stmt::Return(self.parse_return()?)]); return Ok(vec![Stmt::Return(self.parse_return()?)]);
} else { } else {
return Err(( return Err((
@ -235,7 +242,7 @@ impl<'a> Parser<'a> {
// dart-sass seems to special-case the error message here? // dart-sass seems to special-case the error message here?
'!' | '{' => return Err(("expected \"}\".", *pos).into()), '!' | '{' => return Err(("expected \"}\".", *pos).into()),
_ => { _ => {
if self.in_keyframes { if self.flags.contains(Flags::IN_KEYFRAMES) {
match self.is_selector_or_style()? { match self.is_selector_or_style()? {
SelectorOrStyle::Style(property, value) => { SelectorOrStyle::Style(property, value) => {
if let Some(value) = value { if let Some(value) = value {
@ -375,13 +382,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags,
in_function: self.in_function,
in_control_flow: self.in_control_flow,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
}, },
allows_parent, allows_parent,
true, true,
@ -607,13 +611,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags | Flags::IN_CONTROL_FLOW,
in_function: self.in_function,
in_control_flow: true,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse(); .parse();
} }
@ -630,13 +631,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags | Flags::IN_CONTROL_FLOW,
in_function: self.in_function,
in_control_flow: true,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse() .parse()
} }
@ -764,7 +762,7 @@ impl<'a> Parser<'a> {
span: var.span, span: var.span,
}, },
); );
if self.in_function { if self.flags.contains(Flags::IN_FUNCTION) {
let these_stmts = Parser { let these_stmts = Parser {
toks: &mut body.clone().into_iter().peekmore(), toks: &mut body.clone().into_iter().peekmore(),
map: self.map, map: self.map,
@ -774,13 +772,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags | Flags::IN_CONTROL_FLOW,
in_function: self.in_function,
in_control_flow: true,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse()?; .parse()?;
if !these_stmts.is_empty() { if !these_stmts.is_empty() {
@ -797,13 +792,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags | Flags::IN_CONTROL_FLOW,
in_function: self.in_function,
in_control_flow: true,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse()?, .parse()?,
); );
@ -838,7 +830,7 @@ impl<'a> Parser<'a> {
let mut val = self.parse_value_from_vec(cond.clone())?; let mut val = self.parse_value_from_vec(cond.clone())?;
self.scopes.push(self.scopes.last().clone()); self.scopes.push(self.scopes.last().clone());
while val.node.is_true() { while val.node.is_true() {
if self.in_function { if self.flags.contains(Flags::IN_FUNCTION) {
let these_stmts = Parser { let these_stmts = Parser {
toks: &mut body.clone().into_iter().peekmore(), toks: &mut body.clone().into_iter().peekmore(),
map: self.map, map: self.map,
@ -848,13 +840,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags | Flags::IN_CONTROL_FLOW,
in_function: self.in_function,
in_control_flow: true,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse()?; .parse()?;
if !these_stmts.is_empty() { if !these_stmts.is_empty() {
@ -871,13 +860,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags | Flags::IN_CONTROL_FLOW,
in_function: self.in_function,
in_control_flow: true,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse()?, .parse()?,
); );
@ -972,7 +958,7 @@ impl<'a> Parser<'a> {
} }
} }
if self.in_function { if self.flags.contains(Flags::IN_FUNCTION) {
let these_stmts = Parser { let these_stmts = Parser {
toks: &mut body.clone().into_iter().peekmore(), toks: &mut body.clone().into_iter().peekmore(),
map: self.map, map: self.map,
@ -982,13 +968,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags | Flags::IN_CONTROL_FLOW,
in_function: self.in_function,
in_control_flow: true,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse()?; .parse()?;
if !these_stmts.is_empty() { if !these_stmts.is_empty() {
@ -1005,13 +988,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags | Flags::IN_CONTROL_FLOW,
in_function: self.in_function,
in_control_flow: true,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse()?, .parse()?,
); );
@ -1102,13 +1082,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags,
in_function: self.in_function,
in_control_flow: self.in_control_flow,
at_root: false, at_root: false,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse_stmt()?; .parse_stmt()?;
@ -1171,13 +1148,10 @@ impl<'a> Parser<'a> {
super_selectors: &mut NeverEmptyVec::new(at_rule_selector.clone()), super_selectors: &mut NeverEmptyVec::new(at_rule_selector.clone()),
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags,
in_function: self.in_function,
in_control_flow: self.in_control_flow,
at_root: true, at_root: true,
at_root_has_selector, at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse()? .parse()?
.into_iter() .into_iter()
@ -1213,13 +1187,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags,
in_function: self.in_function,
in_control_flow: self.in_control_flow,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse_selector(false, true, String::new())?; .parse_selector(false, true, String::new())?;
@ -1292,13 +1263,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags,
in_function: self.in_function,
in_control_flow: self.in_control_flow,
at_root: false, at_root: false,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse()?; .parse()?;

View File

@ -188,13 +188,10 @@ impl<'a> Parser<'a> {
super_selectors: self.super_selectors, super_selectors: self.super_selectors,
span_before: self.span_before, span_before: self.span_before,
content: self.content, content: self.content,
in_mixin: self.in_mixin, flags: self.flags,
in_function: self.in_function,
in_control_flow: self.in_control_flow,
at_root: self.at_root, at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector, at_root_has_selector: self.at_root_has_selector,
extender: self.extender, extender: self.extender,
in_keyframes: self.in_keyframes,
} }
.parse_value() .parse_value()
} }

View File

@ -11,7 +11,7 @@ use crate::{
Token, Token,
}; };
use super::Parser; use super::{Flags, Parser};
#[derive(Debug)] #[derive(Debug)]
struct VariableValue { struct VariableValue {
@ -46,7 +46,7 @@ impl<'a> Parser<'a> {
} }
if value.default { if value.default {
if self.at_root && !self.in_control_flow { if self.at_root && !self.flags.contains(Flags::IN_CONTROL_FLOW) {
if !self.global_scope.var_exists_no_global(&ident) { if !self.global_scope.var_exists_no_global(&ident) {
self.global_scope.insert_var(ident, value.value); self.global_scope.insert_var(ident, value.value);
} }
@ -60,7 +60,7 @@ impl<'a> Parser<'a> {
} }
} }
} else if self.at_root { } else if self.at_root {
if self.in_control_flow { if self.flags.contains(Flags::IN_CONTROL_FLOW) {
if self.global_scope.var_exists_no_global(&ident) { if self.global_scope.var_exists_no_global(&ident) {
self.global_scope.insert_var(ident, value.value); self.global_scope.insert_var(ident, value.value);
} else { } else {

View File

@ -1,5 +1,4 @@
use codemap::Spanned; use codemap::Spanned;
use peekmore::PeekMoreIterator; use peekmore::PeekMoreIterator;
use crate::{error::SassResult, Token}; use crate::{error::SassResult, Token};
@ -49,10 +48,8 @@ impl ParsedNumber {
pub(crate) fn eat_number<I: Iterator<Item = Token>>( pub(crate) fn eat_number<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>, toks: &mut PeekMoreIterator<I>,
) -> SassResult<Spanned<ParsedNumber>> { ) -> SassResult<Spanned<ParsedNumber>> {
let mut whole = String::with_capacity(1); let mut span = toks.peek().unwrap().pos;
// TODO: merge this span with chars let mut whole = eat_whole_number(toks);
let span = toks.peek().unwrap().pos;
eat_whole_number(toks, &mut whole);
if toks.peek().is_none() { if toks.peek().is_none() {
return Ok(Spanned { return Ok(Spanned {
@ -61,66 +58,64 @@ pub(crate) fn eat_number<I: Iterator<Item = Token>>(
}); });
} }
let mut dec = String::new();
let next_tok = *toks.peek().unwrap(); let next_tok = *toks.peek().unwrap();
if next_tok.kind == '.' { let dec_len = if next_tok.kind == '.' {
toks.next(); toks.next();
eat_whole_number(toks, &mut dec);
let dec = eat_whole_number(toks);
if dec.is_empty() { if dec.is_empty() {
return Err(("Expected digit.", next_tok.pos()).into()); return Err(("Expected digit.", next_tok.pos()).into());
} }
}
whole.push_str(&dec);
dec.len()
} else {
0
};
let mut times_ten = String::new(); let mut times_ten = String::new();
let mut times_ten_is_postive = true; let mut times_ten_is_postive = true;
#[allow(clippy::never_loop)]
loop {
if let Some(Token { kind: 'e', .. }) | Some(Token { kind: 'E', .. }) = toks.peek() { if let Some(Token { kind: 'e', .. }) | Some(Token { kind: 'E', .. }) = toks.peek() {
let t = if let Some(tok) = toks.peek_forward(1) { if let Some(&tok) = toks.peek_next() {
*tok if tok.kind == '-' {
} else {
break;
};
match t.kind {
'-' => {
toks.next(); toks.next();
times_ten_is_postive = false; times_ten_is_postive = false;
}
'0'..='9' => {}
_ => break,
}
toks.next(); toks.next();
times_ten = eat_whole_number(toks);
eat_whole_number(toks, &mut times_ten); if times_ten.is_empty() {
return Err(("Expected digit.", toks.peek().unwrap_or(&tok).pos).into());
}
} else if matches!(tok.kind, '0'..='9') {
toks.next();
times_ten = eat_whole_number(toks);
if times_ten.is_empty() && !times_ten_is_postive { if times_ten.len() > 2 {
return Err(("Expected digit.", toks.peek().unwrap_or(&t).pos).into()); return Err(("Exponent too large.", toks.peek().unwrap_or(&tok).pos).into());
} else if times_ten.len() > 2 {
return Err(("Exponent too large.", toks.peek().unwrap_or(&t).pos).into());
} }
} }
break; }
}
if let Ok(Some(Token { pos, .. })) = toks.peek_previous() {
span = span.merge(*pos);
} }
toks.reset_cursor(); toks.reset_cursor();
whole.push_str(&dec);
Ok(Spanned { Ok(Spanned {
node: ParsedNumber::new(whole, dec.len(), times_ten, times_ten_is_postive), node: ParsedNumber::new(whole, dec_len, times_ten, times_ten_is_postive),
span, span,
}) })
} }
pub(crate) fn eat_whole_number<I: Iterator<Item = Token>>( pub(crate) fn eat_whole_number<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>, toks: &mut PeekMoreIterator<I>,
buf: &mut String, ) -> String {
) { let mut buf = String::new();
while let Some(c) = toks.peek() { while let Some(c) = toks.peek() {
if !c.kind.is_ascii_digit() { if !c.kind.is_ascii_digit() {
break; break;
@ -128,4 +123,5 @@ pub(crate) fn eat_whole_number<I: Iterator<Item = Token>>(
let tok = toks.next().unwrap(); let tok = toks.next().unwrap();
buf.push(tok.kind); buf.push(tok.kind);
} }
buf
} }

View File

@ -343,13 +343,10 @@ impl Value {
super_selectors: parser.super_selectors, super_selectors: parser.super_selectors,
span_before: parser.span_before, span_before: parser.span_before,
content: parser.content, content: parser.content,
in_mixin: parser.in_mixin, flags: parser.flags,
in_function: parser.in_function,
in_control_flow: parser.in_control_flow,
at_root: parser.at_root, at_root: parser.at_root,
at_root_has_selector: parser.at_root_has_selector, at_root_has_selector: parser.at_root_has_selector,
extender: parser.extender, extender: parser.extender,
in_keyframes: parser.in_keyframes,
} }
.parse_selector(allows_parent, true, String::new()) .parse_selector(allows_parent, true, String::new())
} }