initial parsing work for modules

This commit is contained in:
Connor Skees 2020-07-25 19:22:12 -04:00
parent dee6699bde
commit 3a7a3f508a
9 changed files with 118 additions and 13 deletions

View File

@ -80,7 +80,7 @@ grass input.scss
)] )]
#![cfg_attr(feature = "nightly", feature(track_caller))] #![cfg_attr(feature = "nightly", feature(track_caller))]
#![cfg_attr(feature = "profiling", inline(never))] #![cfg_attr(feature = "profiling", inline(never))]
use std::{fs, path::Path}; use std::{collections::HashMap, fs, path::Path};
#[cfg(feature = "wasm")] #[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
@ -292,6 +292,7 @@ pub fn from_path(p: &str, options: &Options) -> Result<String> {
extender: &mut Extender::new(empty_span), extender: &mut Extender::new(empty_span),
content_scopes: &mut Scopes::new(), content_scopes: &mut Scopes::new(),
options, options,
modules: &mut HashMap::new(),
} }
.parse() .parse()
.map_err(|e| raw_to_parse_error(&map, *e, options.unicode_error_messages))?; .map_err(|e| raw_to_parse_error(&map, *e, options.unicode_error_messages))?;
@ -336,6 +337,7 @@ pub fn from_string(p: String, options: &Options) -> Result<String> {
extender: &mut Extender::new(empty_span), extender: &mut Extender::new(empty_span),
content_scopes: &mut Scopes::new(), content_scopes: &mut Scopes::new(),
options, options,
modules: &mut HashMap::new(),
} }
.parse() .parse()
.map_err(|e| raw_to_parse_error(&map, *e, options.unicode_error_messages))?; .map_err(|e| raw_to_parse_error(&map, *e, options.unicode_error_messages))?;
@ -371,6 +373,7 @@ pub fn from_string(p: String) -> std::result::Result<String, JsValue> {
extender: &mut Extender::new(empty_span), extender: &mut Extender::new(empty_span),
content_scopes: &mut Scopes::new(), content_scopes: &mut Scopes::new(),
options: &Options::default(), options: &Options::default(),
modules: &mut HashMap::new(),
} }
.parse() .parse()
.map_err(|e| raw_to_parse_error(&map, *e, true).to_string())?; .map_err(|e| raw_to_parse_error(&map, *e, true).to_string())?;

View File

@ -53,6 +53,7 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse_stmt()?; .parse_stmt()?;
} else { } else {
@ -112,6 +113,7 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse_stmt()?; .parse_stmt()?;
} else { } else {
@ -140,6 +142,7 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse_stmt(); .parse_stmt();
} }
@ -320,8 +323,9 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse()?; .parse_stmt()?;
if !these_stmts.is_empty() { if !these_stmts.is_empty() {
return Ok(these_stmts); return Ok(these_stmts);
} }
@ -342,8 +346,9 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse()?, .parse_stmt()?,
); );
} }
} }
@ -392,8 +397,9 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse()?; .parse_stmt()?;
if !these_stmts.is_empty() { if !these_stmts.is_empty() {
return Ok(these_stmts); return Ok(these_stmts);
} }
@ -414,8 +420,9 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse()?, .parse_stmt()?,
); );
} }
val = self.parse_value_from_vec(cond.clone(), true)?; val = self.parse_value_from_vec(cond.clone(), true)?;
@ -517,8 +524,9 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse()?; .parse_stmt()?;
if !these_stmts.is_empty() { if !these_stmts.is_empty() {
return Ok(these_stmts); return Ok(these_stmts);
} }
@ -539,8 +547,9 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse()?, .parse_stmt()?,
); );
} }
} }

View File

@ -108,8 +108,9 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse()?; .parse_stmt()?;
if entered_scope { if entered_scope {
self.scopes.exit_scope(); self.scopes.exit_scope();

View File

@ -102,6 +102,7 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse(); .parse();
} }

View File

@ -166,6 +166,7 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
}) })
.parse_keyframes_selector()?; .parse_keyframes_selector()?;
@ -197,6 +198,7 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse_stmt()?; .parse_stmt()?;

View File

@ -155,8 +155,9 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse()?; .parse_stmt()?;
self.content.pop(); self.content.pop();
self.scopes.exit_scope(); self.scopes.exit_scope();
@ -207,8 +208,9 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.scopes, content_scopes: self.scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse()? .parse_stmt()?
} else { } else {
Vec::new() Vec::new()
}; };

View File

@ -1,4 +1,4 @@
use std::{convert::TryFrom, path::Path, vec::IntoIter}; use std::{collections::HashMap, convert::TryFrom, path::Path, vec::IntoIter};
use codemap::{CodeMap, Span, Spanned}; use codemap::{CodeMap, Span, Spanned};
use peekmore::{PeekMore, PeekMoreIterator}; use peekmore::{PeekMore, PeekMoreIterator};
@ -86,11 +86,17 @@ pub(crate) struct Parser<'a> {
pub extender: &'a mut Extender, pub extender: &'a mut Extender,
pub options: &'a Options<'a>, pub options: &'a Options<'a>,
pub modules: &'a mut HashMap<String, Scope>,
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
pub fn parse(&mut self) -> SassResult<Vec<Stmt>> { pub fn parse(&mut self) -> SassResult<Vec<Stmt>> {
let mut stmts = Vec::new(); let mut stmts = Vec::new();
self.whitespace();
stmts.append(&mut self.load_modules()?);
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.flags.in_function() && !stmts.is_empty() { if self.flags.in_function() && !stmts.is_empty() {
@ -101,6 +107,76 @@ impl<'a> Parser<'a> {
Ok(stmts) Ok(stmts)
} }
/// Returns any multiline comments that may have been found
/// while loading modules
fn load_modules(&mut self) -> SassResult<Vec<Stmt>> {
let mut comments = Vec::new();
loop {
self.whitespace();
match self.toks.peek() {
Some(Token { kind: '@', .. }) => {
self.toks.advance_cursor();
match AtRuleKind::try_from(&peek_ident_no_interpolation(
self.toks,
false,
self.span_before,
)?)? {
AtRuleKind::Use => {
self.toks.truncate_iterator_to_cursor();
}
_ => {
break;
}
}
self.whitespace_or_comment();
let quote = match self.toks.next() {
Some(Token { kind: q @ '"', .. }) | Some(Token { kind: q @ '\'', .. }) => q,
Some(..) => todo!(),
None => todo!(),
};
let Spanned { node: module, span } = self.parse_quoted_string(quote)?;
let module = module.unquote().to_css_string(span)?;
if let Some(Token { kind: ';', .. }) = self.toks.peek() {
self.toks.next();
} else {
todo!()
}
match module.as_ref() {
"sass:color" => todo!("builtin module `sass:color` not yet implemented"),
"sass:list" => todo!("builtin module `sass:list` not yet implemented"),
"sass:map" => todo!("builtin module `sass:map` not yet implemented"),
"sass:math" => todo!("builtin module `sass:math` not yet implemented"),
"sass:meta" => todo!("builtin module `sass:meta` not yet implemented"),
"sass:selector" => {
todo!("builtin module `sass:selector` not yet implemented")
}
"sass:string" => todo!("builtin module `sass:string` not yet implemented"),
_ => todo!("@use not yet implemented"),
}
}
Some(Token { kind: '/', .. }) => {
self.toks.advance_cursor();
match self.parse_comment()?.node {
Comment::Silent => continue,
Comment::Loud(s) => comments.push(Stmt::Comment(s)),
}
}
Some(..) | None => break,
}
}
self.toks.reset_cursor();
Ok(comments)
}
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() {
@ -196,7 +272,13 @@ impl<'a> Parser<'a> {
AtRuleKind::Unknown(_) => { AtRuleKind::Unknown(_) => {
stmts.push(self.parse_unknown_at_rule(kind_string.node)?) stmts.push(self.parse_unknown_at_rule(kind_string.node)?)
} }
AtRuleKind::Use => todo!("@use not yet implemented"), AtRuleKind::Use => {
return Err((
"@use rules must be written before any other rules.",
kind_string.span,
)
.into())
}
AtRuleKind::Forward => todo!("@forward not yet implemented"), AtRuleKind::Forward => todo!("@forward not yet implemented"),
AtRuleKind::Extend => self.parse_extend()?, AtRuleKind::Extend => self.parse_extend()?,
AtRuleKind::Supports => stmts.push(self.parse_supports()?), AtRuleKind::Supports => stmts.push(self.parse_supports()?),
@ -377,6 +459,7 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
}, },
allows_parent, allows_parent,
true, true,
@ -668,8 +751,9 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse()? .parse_stmt()?
.into_iter() .into_iter()
.filter_map(|s| match s { .filter_map(|s| match s {
Stmt::Style(..) => { Stmt::Style(..) => {
@ -709,6 +793,7 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse_selector(false, true, String::new())?; .parse_selector(false, true, String::new())?;

View File

@ -202,6 +202,7 @@ impl<'a> Parser<'a> {
extender: self.extender, extender: self.extender,
content_scopes: self.content_scopes, content_scopes: self.content_scopes,
options: self.options, options: self.options,
modules: self.modules,
} }
.parse_value(in_paren) .parse_value(in_paren)
} }

View File

@ -477,6 +477,7 @@ impl Value {
extender: parser.extender, extender: parser.extender,
content_scopes: parser.content_scopes, content_scopes: parser.content_scopes,
options: parser.options, options: parser.options,
modules: parser.modules,
} }
.parse_selector(allows_parent, true, String::new())? .parse_selector(allows_parent, true, String::new())?
.0) .0)