Initial implementation of @include and mixin eval
This commit is contained in:
parent
5e58cceae7
commit
2439579d20
@ -58,9 +58,9 @@ impl<'a> Iterator for Lexer<'a> {
|
|||||||
TokenKind::Whitespace(Whitespace::CarriageReturn)
|
TokenKind::Whitespace(Whitespace::CarriageReturn)
|
||||||
}
|
}
|
||||||
'#' => self.lex_hash(),
|
'#' => self.lex_hash(),
|
||||||
'{' => symbol!(self, OpenBrace),
|
'{' => symbol!(self, OpenCurlyBrace),
|
||||||
'*' => symbol!(self, Mul),
|
'*' => symbol!(self, Mul),
|
||||||
'}' => symbol!(self, CloseBrace),
|
'}' => symbol!(self, CloseCurlyBrace),
|
||||||
'&' => symbol!(self, BitAnd),
|
'&' => symbol!(self, BitAnd),
|
||||||
'/' => self.lex_forward_slash(),
|
'/' => self.lex_forward_slash(),
|
||||||
'%' => {
|
'%' => {
|
||||||
|
125
src/main.rs
125
src/main.rs
@ -26,14 +26,13 @@
|
|||||||
clippy::module_name_repetitions
|
clippy::module_name_repetitions
|
||||||
)]
|
)]
|
||||||
// todo! handle erroring on styles at the toplevel
|
// todo! handle erroring on styles at the toplevel
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::iter::{Iterator, Peekable};
|
use std::iter::{Iterator, Peekable};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::common::{AtRule, Keyword, Op, Pos, Symbol, Whitespace};
|
use crate::common::{AtRule, Keyword, Op, Pos, Scope, Symbol, Whitespace};
|
||||||
use crate::css::Css;
|
use crate::css::Css;
|
||||||
use crate::error::SassError;
|
use crate::error::SassError;
|
||||||
use crate::format::PrettyPrinter;
|
use crate::format::PrettyPrinter;
|
||||||
@ -42,6 +41,7 @@ use crate::mixin::{CallArgs, FuncArgs, Mixin};
|
|||||||
use crate::selector::{Attribute, Selector};
|
use crate::selector::{Attribute, Selector};
|
||||||
use crate::style::Style;
|
use crate::style::Style;
|
||||||
use crate::units::Unit;
|
use crate::units::Unit;
|
||||||
|
use crate::utils::{IsWhitespace, devour_whitespace};
|
||||||
|
|
||||||
mod color;
|
mod color;
|
||||||
mod common;
|
mod common;
|
||||||
@ -54,6 +54,7 @@ mod mixin;
|
|||||||
mod selector;
|
mod selector;
|
||||||
mod style;
|
mod style;
|
||||||
mod units;
|
mod units;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
type SassResult<T> = Result<T, SassError>;
|
type SassResult<T> = Result<T, SassError>;
|
||||||
|
|
||||||
@ -139,6 +140,8 @@ pub struct RuleSet {
|
|||||||
enum Expr {
|
enum Expr {
|
||||||
/// A style: `color: red`
|
/// A style: `color: red`
|
||||||
Style(Style),
|
Style(Style),
|
||||||
|
/// A collection of styles, from a mixin or function
|
||||||
|
Styles(Vec<Style>),
|
||||||
/// A full selector `a > h1`
|
/// A full selector `a > h1`
|
||||||
Selector(Selector),
|
Selector(Selector),
|
||||||
/// A variable declaration `$var: 1px`
|
/// A variable declaration `$var: 1px`
|
||||||
@ -201,27 +204,6 @@ struct StyleSheetParser<'a> {
|
|||||||
file: String,
|
file: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, std::default::Default)]
|
|
||||||
pub struct Scope {
|
|
||||||
pub vars: HashMap<String, Vec<Token>>,
|
|
||||||
pub mixins: HashMap<String, Mixin>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Scope {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
vars: HashMap::new(),
|
|
||||||
mixins: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn merge(&mut self, other: Scope) {
|
|
||||||
self.vars.extend(other.vars);
|
|
||||||
self.mixins.extend(other.mixins);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> StyleSheetParser<'a> {
|
impl<'a> StyleSheetParser<'a> {
|
||||||
fn parse_toplevel(mut self) -> SassResult<StyleSheet> {
|
fn parse_toplevel(mut self) -> SassResult<StyleSheet> {
|
||||||
let mut rules = Vec::new();
|
let mut rules = Vec::new();
|
||||||
@ -233,9 +215,8 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
| TokenKind::Symbol(Symbol::Hash)
|
| TokenKind::Symbol(Symbol::Hash)
|
||||||
| TokenKind::Symbol(Symbol::Colon)
|
| TokenKind::Symbol(Symbol::Colon)
|
||||||
| TokenKind::Symbol(Symbol::Mul)
|
| TokenKind::Symbol(Symbol::Mul)
|
||||||
| TokenKind::Symbol(Symbol::Period) => rules.extend(
|
| TokenKind::Symbol(Symbol::Period) => rules
|
||||||
self.eat_rules(&Selector(Vec::new()), &mut self.global_scope.clone()),
|
.extend(self.eat_rules(&Selector(Vec::new()), &mut self.global_scope.clone())),
|
||||||
),
|
|
||||||
TokenKind::Whitespace(_) => {
|
TokenKind::Whitespace(_) => {
|
||||||
self.lexer.next();
|
self.lexer.next();
|
||||||
continue;
|
continue;
|
||||||
@ -262,7 +243,7 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
self.lexer.next();
|
self.lexer.next();
|
||||||
rules.push(Stmt::MultilineComment(comment));
|
rules.push(Stmt::MultilineComment(comment));
|
||||||
}
|
}
|
||||||
TokenKind::AtRule(_) => self.eat_at_rule(),
|
TokenKind::AtRule(_) => {self.eat_at_rule();},
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(Token { pos, .. }) = self.lexer.next() {
|
if let Some(Token { pos, .. }) = self.lexer.next() {
|
||||||
self.error(pos.clone(), "unexpected toplevel token")
|
self.error(pos.clone(), "unexpected toplevel token")
|
||||||
@ -278,28 +259,44 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
fn eat_mixin(&mut self) {
|
fn eat_mixin(&mut self) {
|
||||||
let Token { pos, .. } = self.lexer.next().unwrap();
|
let Token { pos, .. } = self.lexer.next().unwrap();
|
||||||
self.devour_whitespace();
|
self.devour_whitespace();
|
||||||
let name = if let Some(Token { kind: TokenKind::Ident(s), .. }) = self.lexer.next() {
|
let name = if let Some(Token {
|
||||||
|
kind: TokenKind::Ident(s),
|
||||||
|
..
|
||||||
|
}) = self.lexer.next()
|
||||||
|
{
|
||||||
s
|
s
|
||||||
} else {
|
} else {
|
||||||
self.error(pos, "expected identifier after mixin declaration")
|
self.error(pos, "expected identifier after mixin declaration")
|
||||||
};
|
};
|
||||||
self.devour_whitespace();
|
self.devour_whitespace();
|
||||||
let args = match self.lexer.next() {
|
let args = match self.lexer.next() {
|
||||||
Some(Token { kind: TokenKind::Symbol(Symbol::OpenParen), .. }) => self.eat_func_args(),
|
Some(Token {
|
||||||
Some(Token { kind: TokenKind::Symbol(Symbol::OpenBracket), .. }) => FuncArgs::new(),
|
kind: TokenKind::Symbol(Symbol::OpenParen),
|
||||||
|
..
|
||||||
|
}) => self.eat_func_args(),
|
||||||
|
Some(Token {
|
||||||
|
kind: TokenKind::Symbol(Symbol::OpenCurlyBrace),
|
||||||
|
..
|
||||||
|
}) => FuncArgs::new(),
|
||||||
_ => self.error(pos, "expected `(` or `{`"),
|
_ => self.error(pos, "expected `(` or `{`"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = self.lexer.by_ref().take_while(|x| x.kind != TokenKind::Symbol(Symbol::CloseBrace)).collect();
|
let body = self
|
||||||
|
.lexer
|
||||||
|
.by_ref()
|
||||||
|
.take_while(|x| x.kind != TokenKind::Symbol(Symbol::CloseCurlyBrace))
|
||||||
|
.collect();
|
||||||
|
|
||||||
self.global_scope.mixins.insert(name, Mixin::new(self.global_scope.clone(), args, body));
|
self.global_scope
|
||||||
|
.mixins
|
||||||
|
.insert(name, Mixin::new(self.global_scope.clone(), args, body));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat_func_args(&mut self) -> FuncArgs {
|
fn eat_func_args(&mut self) -> FuncArgs {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat_at_rule(&mut self) {
|
fn eat_at_rule(&mut self) -> Option<Expr> {
|
||||||
if let Some(Token {
|
if let Some(Token {
|
||||||
kind: TokenKind::AtRule(ref rule),
|
kind: TokenKind::AtRule(ref rule),
|
||||||
pos,
|
pos,
|
||||||
@ -337,9 +334,39 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
self.debug(pos, &message);
|
self.debug(pos, &message);
|
||||||
}
|
}
|
||||||
AtRule::Mixin => self.eat_mixin(),
|
AtRule::Mixin => self.eat_mixin(),
|
||||||
|
AtRule::Include => return Some(self.eat_include()),
|
||||||
_ => todo!("encountered unimplemented at rule"),
|
_ => todo!("encountered unimplemented at rule"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eat_include(&mut self) -> Expr {
|
||||||
|
self.devour_whitespace();
|
||||||
|
let Token { kind, pos } = self.lexer.next().unwrap();
|
||||||
|
let name = if let TokenKind::Ident(s) = kind {
|
||||||
|
s
|
||||||
|
} else {
|
||||||
|
self.error(pos, "expected identifier")
|
||||||
|
};
|
||||||
|
|
||||||
|
self.devour_whitespace();
|
||||||
|
|
||||||
|
match self.lexer.next() {
|
||||||
|
Some(Token { kind: TokenKind::Symbol(Symbol::SemiColon), .. }) => {},
|
||||||
|
Some(Token { kind: TokenKind::Symbol(Symbol::OpenParen), .. }) => {},
|
||||||
|
Some(Token { pos, .. }) => self.error(pos, "expected `(` or `;`"),
|
||||||
|
None => self.error(pos, "unexpected EOF"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut mixin = if let Some(m) = self.global_scope.mixins.get(&name) {
|
||||||
|
m.clone()
|
||||||
|
} else {
|
||||||
|
self.error(pos, "expected identifier")
|
||||||
|
};
|
||||||
|
let styles = mixin.eval();
|
||||||
|
self.devour_whitespace();
|
||||||
|
Expr::Styles(styles)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat_variable_value(&mut self) -> Vec<Token> {
|
fn eat_variable_value(&mut self) -> Vec<Token> {
|
||||||
@ -357,7 +384,8 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
} = tok
|
} = tok
|
||||||
{
|
{
|
||||||
iter2.extend(
|
iter2.extend(
|
||||||
self.global_scope.vars
|
self.global_scope
|
||||||
|
.vars
|
||||||
.get(name)
|
.get(name)
|
||||||
.unwrap_or_else(|| self.error(pos, "Undefined variable"))
|
.unwrap_or_else(|| self.error(pos, "Undefined variable"))
|
||||||
.clone(),
|
.clone(),
|
||||||
@ -371,15 +399,12 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
|
|
||||||
fn eat_func_call(&mut self) {}
|
fn eat_func_call(&mut self) {}
|
||||||
|
|
||||||
fn eat_rules(
|
fn eat_rules(&mut self, super_selector: &Selector, scope: &mut Scope) -> Vec<Stmt> {
|
||||||
&mut self,
|
|
||||||
super_selector: &Selector,
|
|
||||||
scope: &mut Scope,
|
|
||||||
) -> Vec<Stmt> {
|
|
||||||
let mut stmts = Vec::new();
|
let mut stmts = Vec::new();
|
||||||
while let Ok(tok) = self.eat_expr(scope, super_selector) {
|
while let Ok(tok) = self.eat_expr(scope, super_selector) {
|
||||||
match tok {
|
match tok {
|
||||||
Expr::Style(s) => stmts.push(Stmt::Style(s)),
|
Expr::Style(s) => stmts.push(Stmt::Style(s)),
|
||||||
|
Expr::Styles(s) => stmts.extend(s.iter().map(|s| Stmt::Style(s.clone()))),
|
||||||
Expr::Selector(s) => {
|
Expr::Selector(s) => {
|
||||||
self.scope += 1;
|
self.scope += 1;
|
||||||
let rules = self.eat_rules(&super_selector.clone().zip(s.clone()), scope);
|
let rules = self.eat_rules(&super_selector.clone().zip(s.clone()), scope);
|
||||||
@ -407,20 +432,16 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
stmts
|
stmts
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat_expr(
|
fn eat_expr(&mut self, scope: &Scope, super_selector: &Selector) -> Result<Expr, ()> {
|
||||||
&mut self,
|
|
||||||
scope: &Scope,
|
|
||||||
super_selector: &Selector,
|
|
||||||
) -> Result<Expr, ()> {
|
|
||||||
let mut values = Vec::with_capacity(5);
|
let mut values = Vec::with_capacity(5);
|
||||||
while let Some(tok) = self.lexer.peek() {
|
while let Some(tok) = self.lexer.peek() {
|
||||||
match &tok.kind {
|
match &tok.kind {
|
||||||
TokenKind::Symbol(Symbol::SemiColon) | TokenKind::Symbol(Symbol::CloseBrace) => {
|
TokenKind::Symbol(Symbol::SemiColon) | TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
|
||||||
self.lexer.next();
|
self.lexer.next();
|
||||||
self.devour_whitespace();
|
self.devour_whitespace();
|
||||||
return Ok(Expr::Style(Style::from_tokens(&values, scope)?));
|
return Ok(Expr::Style(Style::from_tokens(&values, scope)?));
|
||||||
}
|
}
|
||||||
TokenKind::Symbol(Symbol::OpenBrace) => {
|
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
||||||
self.lexer.next();
|
self.lexer.next();
|
||||||
self.devour_whitespace();
|
self.devour_whitespace();
|
||||||
return Ok(Expr::Selector(Selector::from_tokens(
|
return Ok(Expr::Selector(Selector::from_tokens(
|
||||||
@ -466,10 +487,14 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
values.push(tok.clone())
|
values.push(tok.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TokenKind::AtRule(_) => self.eat_at_rule(),
|
TokenKind::AtRule(_) => {
|
||||||
|
if let Some(a) = self.eat_at_rule() {
|
||||||
|
return Ok(a);
|
||||||
|
}
|
||||||
|
},
|
||||||
TokenKind::Interpolation => {
|
TokenKind::Interpolation => {
|
||||||
while let Some(tok) = self.lexer.next() {
|
while let Some(tok) = self.lexer.next() {
|
||||||
if tok.kind == TokenKind::Symbol(Symbol::CloseBrace) {
|
if tok.kind == TokenKind::Symbol(Symbol::CloseCurlyBrace) {
|
||||||
values.push(tok);
|
values.push(tok);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -546,9 +571,9 @@ fn main() -> SassResult<()> {
|
|||||||
let mut stdout = std::io::BufWriter::new(std::io::stdout());
|
let mut stdout = std::io::BufWriter::new(std::io::stdout());
|
||||||
let s = StyleSheet::from_path("input.scss")?;
|
let s = StyleSheet::from_path("input.scss")?;
|
||||||
// dbg!(s);
|
// dbg!(s);
|
||||||
// s.pretty_print(&mut stdout)?;
|
s.pretty_print(&mut stdout)?;
|
||||||
// s.pretty_print_selectors(&mut stdout)?;
|
// s.pretty_print_selectors(&mut stdout)?;
|
||||||
s.print_as_css(&mut stdout)?;
|
// s.print_as_css(&mut stdout)?;
|
||||||
// dbg!(Css::from_stylesheet(s));
|
// dbg!(Css::from_stylesheet(s));
|
||||||
// println!("{}", s);
|
// println!("{}", s);
|
||||||
// drop(input);
|
// drop(input);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::common::Symbol;
|
use crate::common::{Scope, Symbol};
|
||||||
use crate::{Scope, Token, TokenKind};
|
use crate::{Token, TokenKind};
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
@ -189,7 +189,7 @@ impl<'a> SelectorParser<'a> {
|
|||||||
let toks = self
|
let toks = self
|
||||||
.tokens
|
.tokens
|
||||||
.by_ref()
|
.by_ref()
|
||||||
.take_while(|x| x.kind != TokenKind::Symbol(Symbol::CloseBrace))
|
.take_while(|x| x.kind != TokenKind::Symbol(Symbol::CloseCurlyBrace))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<Token>>(); //.iter().peekable();
|
.collect::<Vec<Token>>(); //.iter().peekable();
|
||||||
let mut toks = toks.iter().peekable();
|
let mut toks = toks.iter().peekable();
|
||||||
@ -248,9 +248,11 @@ impl<'a> SelectorParser<'a> {
|
|||||||
fn deref_variable(&mut self, variable: &TokenKind) -> Vec<Token> {
|
fn deref_variable(&mut self, variable: &TokenKind) -> Vec<Token> {
|
||||||
let mut val = Vec::with_capacity(25);
|
let mut val = Vec::with_capacity(25);
|
||||||
let v = match variable {
|
let v = match variable {
|
||||||
TokenKind::Variable(ref v) => {
|
TokenKind::Variable(ref v) => self
|
||||||
self.scope.vars.get(v).expect("todo! expected variable to exist")
|
.scope
|
||||||
}
|
.vars
|
||||||
|
.get(v)
|
||||||
|
.expect("todo! expected variable to exist"),
|
||||||
_ => todo!("expected variable"),
|
_ => todo!("expected variable"),
|
||||||
}
|
}
|
||||||
.iter()
|
.iter()
|
||||||
|
16
src/style.rs
16
src/style.rs
@ -1,5 +1,5 @@
|
|||||||
use crate::common::Symbol;
|
use crate::common::{Scope, Symbol};
|
||||||
use crate::{Scope, Token, TokenKind};
|
use crate::{Token, TokenKind};
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
@ -40,9 +40,11 @@ impl<'a> StyleParser<'a> {
|
|||||||
fn deref_variable(&mut self, variable: &TokenKind) -> String {
|
fn deref_variable(&mut self, variable: &TokenKind) -> String {
|
||||||
let mut val = String::with_capacity(25);
|
let mut val = String::with_capacity(25);
|
||||||
let mut v = match variable {
|
let mut v = match variable {
|
||||||
TokenKind::Variable(ref v) => {
|
TokenKind::Variable(ref v) => self
|
||||||
self.scope.vars.get(v).expect("todo! expected variable to exist")
|
.scope
|
||||||
}
|
.vars
|
||||||
|
.get(v)
|
||||||
|
.expect("todo! expected variable to exist"),
|
||||||
_ => panic!("expected variable"),
|
_ => panic!("expected variable"),
|
||||||
}
|
}
|
||||||
.iter()
|
.iter()
|
||||||
@ -79,8 +81,8 @@ impl<'a> StyleParser<'a> {
|
|||||||
let mut val = String::new();
|
let mut val = String::new();
|
||||||
while let Some(Token { kind, .. }) = self.tokens.next() {
|
while let Some(Token { kind, .. }) = self.tokens.next() {
|
||||||
match &kind {
|
match &kind {
|
||||||
TokenKind::Symbol(Symbol::CloseBrace) => break,
|
TokenKind::Symbol(Symbol::CloseCurlyBrace) => break,
|
||||||
TokenKind::Symbol(Symbol::OpenBrace) => todo!("invalid character in interpolation"),
|
TokenKind::Symbol(Symbol::OpenCurlyBrace) => todo!("invalid character in interpolation"),
|
||||||
TokenKind::Variable(_) => val.push_str(&self.deref_variable(kind)),
|
TokenKind::Variable(_) => val.push_str(&self.deref_variable(kind)),
|
||||||
_ => val.push_str(&kind.to_string()),
|
_ => val.push_str(&kind.to_string()),
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user