reduce size of parse::Stmt

This commit is contained in:
ConnorSkees 2020-06-25 00:27:24 -04:00
parent 177cacd9c9
commit e12d3a581d
10 changed files with 149 additions and 111 deletions

69
src/atrule/media.rs Normal file
View File

@ -0,0 +1,69 @@
use std::fmt;
use crate::{parse::Stmt, selector::Selector};
#[derive(Debug, Clone)]
pub(crate) struct MediaRule {
pub super_selector: Selector,
pub query: String,
pub body: Vec<Stmt>,
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub(crate) struct MediaQuery {
/// The modifier, probably either "not" or "only".
///
/// This may be `None` if no modifier is in use.
pub modifier: Option<String>,
/// The media type, for example "screen" or "print".
///
/// This may be `None`. If so, `self.features` will not be empty.
pub media_type: Option<String>,
/// Feature queries, including parentheses.
pub features: Vec<String>,
}
#[allow(dead_code)]
impl MediaQuery {
pub fn is_condition(&self) -> bool {
self.modifier.is_none() && self.media_type.is_none()
}
pub fn matches_all_types(&self) -> bool {
self.media_type.is_none()
|| self
.media_type
.as_ref()
.map_or(false, |v| v.to_ascii_lowercase() == "all")
}
pub fn condition(features: Vec<String>) -> Self {
Self {
modifier: None,
media_type: None,
features,
}
}
#[allow(dead_code, unused_variables)]
pub fn merge(other: &Self) -> Self {
todo!()
}
}
impl fmt::Display for MediaQuery {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(modifier) = &self.modifier {
f.write_str(modifier)?;
}
if let Some(media_type) = &self.media_type {
f.write_str(media_type)?;
if !&self.features.is_empty() {
f.write_str(" and ")?;
}
}
f.write_str(&self.features.join(" and "))
}
}

View File

@ -1,7 +1,12 @@
pub(crate) use function::Function; pub(crate) use function::Function;
pub(crate) use kind::AtRuleKind; pub(crate) use kind::AtRuleKind;
pub(crate) use mixin::Mixin; pub(crate) use mixin::Mixin;
pub(crate) use supports::SupportsRule;
pub(crate) use unknown::UnknownAtRule;
mod function; mod function;
mod kind; mod kind;
pub mod media;
mod mixin; mod mixin;
mod supports;
mod unknown;

7
src/atrule/supports.rs Normal file
View File

@ -0,0 +1,7 @@
use crate::parse::Stmt;
#[derive(Debug, Clone)]
pub(crate) struct SupportsRule {
pub params: String,
pub body: Vec<Stmt>,
}

9
src/atrule/unknown.rs Normal file
View File

@ -0,0 +1,9 @@
use crate::{parse::Stmt, selector::Selector};
#[derive(Debug, Clone)]
pub(crate) struct UnknownAtRule {
pub name: String,
pub super_selector: Selector,
pub params: String,
pub body: Vec<Stmt>,
}

View File

@ -3,7 +3,14 @@ use std::io::Write;
use codemap::CodeMap; use codemap::CodeMap;
use crate::{error::SassResult, parse::Stmt, selector::Extender, selector::Selector, style::Style}; use crate::{
atrule::{media::MediaRule, SupportsRule, UnknownAtRule},
error::SassResult,
parse::Stmt,
selector::Extender,
selector::Selector,
style::Style,
};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Toplevel { enum Toplevel {
@ -23,7 +30,7 @@ enum Toplevel {
body: Vec<Stmt>, body: Vec<Stmt>,
}, },
Newline, Newline,
Style(Box<Style>), Style(Style),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -92,17 +99,22 @@ impl Css {
for rule in body { for rule in body {
match rule { match rule {
Stmt::RuleSet { .. } => vals.extend(self.parse_stmt(rule, extender)?), Stmt::RuleSet { .. } => vals.extend(self.parse_stmt(rule, extender)?),
Stmt::Style(s) => vals.get_mut(0).unwrap().push_style(*s)?, Stmt::Style(s) => vals.get_mut(0).unwrap().push_style(s)?,
Stmt::Comment(s) => vals.get_mut(0).unwrap().push_comment(s), Stmt::Comment(s) => vals.get_mut(0).unwrap().push_comment(s),
Stmt::Media { query, body, .. } => { Stmt::Media(m) => {
let MediaRule { query, body, .. } = *m;
vals.push(Toplevel::Media { query, body }) vals.push(Toplevel::Media { query, body })
} }
Stmt::Supports { params, body, .. } => { Stmt::Supports(s) => {
let SupportsRule { params, body, .. } = *s;
vals.push(Toplevel::Supports { params, body }) vals.push(Toplevel::Supports { params, body })
} }
Stmt::UnknownAtRule { Stmt::UnknownAtRule(u) => {
let UnknownAtRule {
params, body, name, .. params, body, name, ..
} => vals.push(Toplevel::UnknownAtRule { params, body, name }), } = *u;
vals.push(Toplevel::UnknownAtRule { params, body, name })
}
Stmt::Return(..) => unreachable!(), Stmt::Return(..) => unreachable!(),
Stmt::AtRoot { body } => body Stmt::AtRoot { body } => body
.into_iter() .into_iter()
@ -114,11 +126,20 @@ impl Css {
} }
Stmt::Comment(s) => vec![Toplevel::MultilineComment(s)], Stmt::Comment(s) => vec![Toplevel::MultilineComment(s)],
Stmt::Style(s) => vec![Toplevel::Style(s)], Stmt::Style(s) => vec![Toplevel::Style(s)],
Stmt::Media { query, body, .. } => vec![Toplevel::Media { query, body }], Stmt::Media(m) => {
Stmt::Supports { params, body, .. } => vec![Toplevel::Supports { params, body }], let MediaRule { query, body, .. } = *m;
Stmt::UnknownAtRule { vec![Toplevel::Media { query, body }]
params, name, body, .. }
} => vec![Toplevel::UnknownAtRule { params, name, body }], Stmt::Supports(s) => {
let SupportsRule { params, body, .. } = *s;
vec![Toplevel::Supports { params, body }]
}
Stmt::UnknownAtRule(u) => {
let UnknownAtRule {
params, body, name, ..
} = *u;
vec![Toplevel::UnknownAtRule { params, name, body }]
}
Stmt::Return(..) => unreachable!("@return: {:?}", stmt), Stmt::Return(..) => unreachable!("@return: {:?}", stmt),
Stmt::AtRoot { .. } => unreachable!("@at-root: {:?}", stmt), Stmt::AtRoot { .. } => unreachable!("@at-root: {:?}", stmt),
}) })

View File

@ -82,13 +82,13 @@ impl<'a> Parser<'a> {
Ok(()) Ok(())
} }
pub(super) fn parse_return(&mut self) -> SassResult<Value> { pub(super) fn parse_return(&mut self) -> SassResult<Box<Value>> {
let toks = read_until_semicolon_or_closing_curly_brace(self.toks)?; let toks = read_until_semicolon_or_closing_curly_brace(self.toks)?;
let v = self.parse_value_from_vec(toks)?; let v = self.parse_value_from_vec(toks)?;
if let Some(Token { kind: ';', .. }) = self.toks.peek() { if let Some(Token { kind: ';', .. }) = self.toks.peek() {
self.toks.next(); self.toks.next();
} }
Ok(v.node) Ok(Box::new(v.node))
} }
pub fn eval_function(&mut self, mut function: Function, args: CallArgs) -> SassResult<Value> { pub fn eval_function(&mut self, mut function: Function, args: CallArgs) -> SassResult<Value> {
@ -117,7 +117,7 @@ impl<'a> Parser<'a> {
.pop() .pop()
.ok_or(("Function finished without @return.", self.span_before))? .ok_or(("Function finished without @return.", self.span_before))?
{ {
Stmt::Return(v) => Ok(v), Stmt::Return(v) => Ok(*v),
_ => todo!("should be unreachable"), _ => todo!("should be unreachable"),
} }
} }

View File

@ -1,5 +1,3 @@
use std::fmt;
use crate::{ use crate::{
error::SassResult, error::SassResult,
utils::{is_name_start, peek_ident_no_interpolation, read_until_closing_paren}, utils::{is_name_start, peek_ident_no_interpolation, read_until_closing_paren},
@ -8,65 +6,6 @@ use crate::{
use super::Parser; use super::Parser;
#[derive(Debug, Eq, PartialEq, Clone)]
pub(super) struct MediaQuery {
/// The modifier, probably either "not" or "only".
///
/// This may be `None` if no modifier is in use.
modifier: Option<String>,
/// The media type, for example "screen" or "print".
///
/// This may be `None`. If so, `self.features` will not be empty.
media_type: Option<String>,
/// Feature queries, including parentheses.
features: Vec<String>,
}
#[allow(dead_code)]
impl MediaQuery {
pub fn is_condition(&self) -> bool {
self.modifier.is_none() && self.media_type.is_none()
}
pub fn matches_all_types(&self) -> bool {
self.media_type.is_none()
|| self
.media_type
.as_ref()
.map_or(false, |v| v.to_ascii_lowercase() == "all")
}
pub fn condition(features: Vec<String>) -> Self {
Self {
modifier: None,
media_type: None,
features,
}
}
#[allow(dead_code, unused_variables)]
pub fn merge(other: &Self) -> Self {
todo!()
}
}
impl fmt::Display for MediaQuery {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(modifier) = &self.modifier {
f.write_str(modifier)?;
}
if let Some(media_type) = &self.media_type {
f.write_str(media_type)?;
if !&self.features.is_empty() {
f.write_str(" and ")?;
}
}
f.write_str(&self.features.join(" and "))
}
}
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
pub fn scan_identifier(&mut self, ident: &str) -> SassResult<bool> { pub fn scan_identifier(&mut self, ident: &str) -> SassResult<bool> {
let peeked_identifier = let peeked_identifier =

View File

@ -5,7 +5,7 @@ use num_traits::cast::ToPrimitive;
use peekmore::{PeekMore, PeekMoreIterator}; use peekmore::{PeekMore, PeekMoreIterator};
use crate::{ use crate::{
atrule::AtRuleKind, atrule::{media::MediaRule, AtRuleKind, SupportsRule, UnknownAtRule},
common::{Brackets, ListSeparator}, common::{Brackets, ListSeparator},
error::SassResult, error::SassResult,
scope::Scope, scope::Scope,
@ -46,27 +46,15 @@ pub(crate) enum Stmt {
selector: ExtendedSelector, selector: ExtendedSelector,
body: Vec<Self>, body: Vec<Self>,
}, },
Style(Box<Style>), Style(Style),
Media { Media(Box<MediaRule>),
super_selector: Selector, UnknownAtRule(Box<UnknownAtRule>),
query: String, Supports(Box<SupportsRule>),
body: Vec<Stmt>,
},
UnknownAtRule {
name: String,
super_selector: Selector,
params: String,
body: Vec<Stmt>,
},
Supports {
params: String,
body: Vec<Stmt>,
},
AtRoot { AtRoot {
body: Vec<Stmt>, body: Vec<Stmt>,
}, },
Comment(String), Comment(String),
Return(Value), Return(Box<Value>),
} }
/// 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
@ -252,12 +240,12 @@ impl<'a> Parser<'a> {
let styles = if let Some(value) = value { let styles = if let Some(value) = value {
vec![Style { vec![Style {
property, property,
value: *value, value: value,
}] }]
} else { } else {
self.parse_style_group(property)? self.parse_style_group(property)?
}; };
stmts.extend(styles.into_iter().map(Box::new).map(Stmt::Style)); stmts.extend(styles.into_iter().map(Stmt::Style));
} }
SelectorOrStyle::Selector(init) => { SelectorOrStyle::Selector(init) => {
let at_root = self.at_root; let at_root = self.at_root;
@ -1003,12 +991,12 @@ impl<'a> Parser<'a> {
self.whitespace(); self.whitespace();
if let Some(Token { kind: ';', .. }) | None = self.toks.peek() { if let Some(Token { kind: ';', .. }) | None = self.toks.peek() {
self.toks.next(); self.toks.next();
return Ok(Stmt::UnknownAtRule { return Ok(Stmt::UnknownAtRule(Box::new(UnknownAtRule {
name, name,
super_selector: Selector::new(self.span_before), super_selector: Selector::new(self.span_before),
params: String::new(), params: String::new(),
body: Vec::new(), body: Vec::new(),
}); })));
} }
while let Some(tok) = self.toks.next() { while let Some(tok) = self.toks.next() {
match tok.kind { match tok.kind {
@ -1053,12 +1041,12 @@ impl<'a> Parser<'a> {
body.append(&mut rules); body.append(&mut rules);
Ok(Stmt::UnknownAtRule { Ok(Stmt::UnknownAtRule(Box::new(UnknownAtRule {
name, name,
super_selector: Selector::new(self.span_before), super_selector: Selector::new(self.span_before),
params: params.trim().to_owned(), params: params.trim().to_owned(),
body, body,
}) })))
} }
fn parse_media(&mut self) -> SassResult<Stmt> { fn parse_media(&mut self) -> SassResult<Stmt> {
@ -1107,11 +1095,11 @@ impl<'a> Parser<'a> {
body.append(&mut rules); body.append(&mut rules);
Ok(Stmt::Media { Ok(Stmt::Media(Box::new(MediaRule {
super_selector: Selector::new(self.span_before), super_selector: Selector::new(self.span_before),
query, query,
body, body,
}) })))
} }
fn parse_at_root(&mut self) -> SassResult<Vec<Stmt>> { fn parse_at_root(&mut self) -> SassResult<Vec<Stmt>> {
@ -1292,10 +1280,10 @@ impl<'a> Parser<'a> {
body.append(&mut rules); body.append(&mut rules);
Ok(Stmt::Supports { Ok(Stmt::Supports(Box::new(SupportsRule {
params: params.trim().to_owned(), params: params.trim().to_owned(),
body, body,
}) })))
} }
#[allow(dead_code, clippy::unused_self)] #[allow(dead_code, clippy::unused_self)]

View File

@ -198,7 +198,7 @@ impl<'a> Parser<'a> {
continue; continue;
} }
} }
let value = self.parse_style_value()?; let value = Box::new(self.parse_style_value()?);
match self.toks.peek() { match self.toks.peek() {
Some(Token { kind: '}', .. }) => { Some(Token { kind: '}', .. }) => {
styles.push(Style { property, value }); styles.push(Style { property, value });
@ -246,7 +246,7 @@ impl<'a> Parser<'a> {
'{' => { '{' => {
let mut v = vec![Style { let mut v = vec![Style {
property: super_property.clone(), property: super_property.clone(),
value, value: Box::new(value),
}]; }];
v.append(&mut self.parse_style_group(super_property)?); v.append(&mut self.parse_style_group(super_property)?);
return Ok(v); return Ok(v);
@ -255,7 +255,7 @@ impl<'a> Parser<'a> {
} }
return Ok(vec![Style { return Ok(vec![Style {
property: super_property, property: super_property,
value, value: Box::new(value),
}]); }]);
} }
} }

View File

@ -6,7 +6,7 @@ use crate::{error::SassResult, value::Value};
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct Style { pub(crate) struct Style {
pub property: String, pub property: String,
pub value: Spanned<Value>, pub value: Box<Spanned<Value>>,
} }
impl Style { impl Style {
@ -21,10 +21,10 @@ impl Style {
pub(crate) fn eval(self) -> SassResult<Self> { pub(crate) fn eval(self) -> SassResult<Self> {
Ok(Style { Ok(Style {
property: self.property, property: self.property,
value: Spanned { value: Box::new(Spanned {
span: self.value.span, span: self.value.span,
node: self.value.node.eval(self.value.span)?.node, node: self.value.node.eval(self.value.span)?.node,
}, }),
}) })
} }
} }