diff --git a/src/common.rs b/src/common.rs index e155b32..eade3b0 100644 --- a/src/common.rs +++ b/src/common.rs @@ -487,3 +487,10 @@ impl Scope { self.mixins.extend(other.mixins); } } + +#[derive(Debug)] +pub(crate) enum Printer { + Error(Pos, String), + Warn(Pos, String), + Debug(Pos, String), +} diff --git a/src/main.rs b/src/main.rs index 3560675..0306cca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,12 +31,12 @@ use std::io; use std::iter::{Iterator, Peekable}; use std::path::Path; -use crate::common::{AtRule, Keyword, Op, Pos, Scope, Symbol, Whitespace}; +use crate::common::{AtRule, Keyword, Op, Pos, Printer, Scope, Symbol, Whitespace}; use crate::css::Css; use crate::error::SassError; use crate::format::PrettyPrinter; use crate::lexer::Lexer; -use crate::mixin::{FuncArgs, Mixin}; +use crate::mixin::{CallArg, CallArgs, FuncArg, FuncArgs, Mixin}; use crate::selector::{Attribute, Selector}; use crate::style::Style; use crate::units::Unit; @@ -296,8 +296,6 @@ impl<'a> StyleSheetParser<'a> { Ok(StyleSheet { rules }) } - fn eat_func_call(&mut self) {} - fn eat_rules(&mut self, super_selector: &Selector, scope: &mut Scope) -> Vec { let mut stmts = Vec::new(); while let Ok(tok) = eat_expr(&mut self.lexer, scope, super_selector) { @@ -337,7 +335,7 @@ impl<'a> StyleSheetParser<'a> { } } -fn eat_include>( +fn eat_include>( toks: &mut Peekable, scope: &Scope, super_selector: &Selector, @@ -353,34 +351,129 @@ fn eat_include>( devour_whitespace(toks); - match toks.next() { + let args = match toks.next() { Some(Token { kind: TokenKind::Symbol(Symbol::SemiColon), .. - }) => {} + }) => CallArgs::new(), Some(Token { kind: TokenKind::Symbol(Symbol::OpenParen), .. - }) => {} + }) => eat_call_args(toks), Some(Token { pos, .. }) => return Err((pos, "expected `(` or `;`")), None => return Err((pos, "unexpected EOF")), + }; + + devour_whitespace(toks); + + if !args.is_empty() { + if let Some(tok) = toks.next() { + assert_eq!(tok.kind, TokenKind::Symbol(Symbol::SemiColon)); + } } + devour_whitespace(toks); + let mut mixin = if let Some(m) = scope.mixins.get(&name) { m.clone() } else { return Err((pos, "expected identifier")); }; - let rules = mixin.eval(super_selector, &mut scope.clone()); - devour_whitespace(toks); + let rules = mixin.call_with_args(&args).eval(super_selector, &mut scope.clone()); Ok(rules) } -fn eat_func_args() -> FuncArgs { - todo!() +fn eat_func_args>(toks: &mut Peekable) -> FuncArgs { + let mut args: Vec = Vec::new(); + + devour_whitespace(toks); + while let Some(Token { kind, .. }) = toks.next() { + let name = match kind { + TokenKind::Variable(v) => v, + TokenKind::Symbol(Symbol::CloseParen) => break, + _ => todo!(), + }; + devour_whitespace(toks); + let kind = if let Some(Token { kind, .. }) = toks.next() { + kind + } else { + todo!() + }; + match kind { + TokenKind::Symbol(Symbol::Colon) => { + todo!("handle default values") + // let mut val: Vec = Vec::new(); + // while let Some(tok) = toks.next() { + // match &kind { + // _ => val.push(tok), + // } + // } + } + TokenKind::Symbol(Symbol::Period) => todo!("handle varargs"), + TokenKind::Symbol(Symbol::CloseParen) => { + args.push(FuncArg { + name, + default: None, + }); + break; + } + TokenKind::Symbol(Symbol::Comma) => args.push(FuncArg { + name, + default: None, + }), + _ => {} + } + devour_whitespace(toks); + } + devour_whitespace(toks); + if let Some(Token { kind: TokenKind::Symbol(Symbol::OpenCurlyBrace), .. }) = toks.next() {} else { + todo!("expected `{{` after mixin args") + } + FuncArgs(args) } -fn parse_mixin>( +fn eat_call_args>(toks: &mut Peekable) -> CallArgs { + let mut args: Vec = Vec::new(); + devour_whitespace(toks); + let mut name: Option = None; + let mut val = Vec::new(); + while let Some(Token { kind, pos }) = toks.next() { + match kind { + TokenKind::Variable(v) => name = Some(v), + TokenKind::Symbol(Symbol::Colon) => { + todo!("handle default values") + // let mut val: Vec = Vec::new(); + // while let Some(Token { kind, .. }) = toks.next() { + // match &kind { + // _ => {} + // } + // } + } + TokenKind::Symbol(Symbol::CloseParen) => { + args.push(CallArg { + name: name.clone(), + val: val.clone(), + }); + break; + }, + TokenKind::Symbol(Symbol::Comma) => { + args.push(CallArg { + name: name.clone(), + val: val.clone(), + }); + if let Some(ref mut s) = name { + s.clear(); + } + val.clear(); + } + _ => val.push(Token { kind, pos }), + } + devour_whitespace(toks); + } + CallArgs(args) +} + +fn parse_mixin>( toks: &mut Peekable, scope: Scope, ) -> Result<(String, Mixin), Printer> { @@ -403,7 +496,7 @@ fn parse_mixin>( Some(Token { kind: TokenKind::Symbol(Symbol::OpenParen), .. - }) => eat_func_args(), + }) => eat_func_args(toks), Some(Token { kind: TokenKind::Symbol(Symbol::OpenCurlyBrace), .. @@ -430,14 +523,7 @@ fn parse_mixin>( Ok((name, Mixin::new(scope, args, body))) } -#[derive(Debug)] -enum Printer { - Error(Pos, String), - Warn(Pos, String), - Debug(Pos, String), -} - -fn eat_at_rule>( +fn eat_at_rule>( rule: AtRule, pos: Pos, toks: &mut Peekable, @@ -478,7 +564,7 @@ fn eat_at_rule>( } } -pub(crate) fn eat_expr>( +pub(crate) fn eat_expr>( toks: &mut Peekable, scope: &Scope, super_selector: &Selector, @@ -569,7 +655,7 @@ pub(crate) fn eat_expr>( } _ => { if let Some(tok) = toks.next() { - values.push(tok.clone()) + values.push(tok) } else { unsafe { std::hint::unreachable_unchecked() } } diff --git a/src/mixin.rs b/src/mixin.rs index e10f769..93b0bb7 100644 --- a/src/mixin.rs +++ b/src/mixin.rs @@ -23,8 +23,21 @@ impl Mixin { } } + pub fn call_with_args(&mut self, args: &CallArgs) -> &mut Mixin { + for (idx, arg) in args.0.iter().enumerate() { + if arg.is_named() { + todo!("keyword args") + } else { + // dbg!(&self.args.0[idx].name.clone()); + self.scope.vars.insert(self.args.0[idx].name.clone(), arg.val.clone()); + } + } + self + } + pub fn eval(&mut self, super_selector: &Selector, scope: &mut Scope) -> Vec { let mut stmts = Vec::new(); + // dbg!(&scope); while let Ok(expr) = eat_expr(&mut self.body, scope, super_selector) { match expr { Expr::Style(s) => stmts.push(Stmt::Style(s)), @@ -56,7 +69,13 @@ impl Mixin { } #[derive(Debug, Clone, Eq, PartialEq)] -pub struct FuncArgs(pub Vec<(Option, Vec)>); +pub struct FuncArgs(pub Vec); + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct FuncArg { + pub name: String, + pub default: Option>, +} impl FuncArgs { pub const fn new() -> Self { @@ -65,4 +84,26 @@ impl FuncArgs { } #[derive(Debug, Clone)] -pub struct CallArgs(Vec<(Option, Vec)>); +pub struct CallArgs(pub Vec); + +#[derive(Debug, Clone)] +pub struct CallArg { + pub name: Option, + pub val: Vec, +} + +impl CallArg { + pub fn is_named(&self) -> bool { + self.name.is_some() + } +} + +impl CallArgs { + pub const fn new() -> Self { + CallArgs(Vec::new()) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} \ No newline at end of file diff --git a/src/units.rs b/src/units.rs index fec43de..e675769 100644 --- a/src/units.rs +++ b/src/units.rs @@ -126,7 +126,8 @@ impl TryFrom<&str> for Unit { b"kHz" => Ok(Unit::Khz), b"dpi" => Ok(Unit::Dpi), b"dpcm" => Ok(Unit::Dpcm), - b"dppx" | b"x" => Ok(Unit::Dppx), + b"dppx" => Ok(Unit::Dppx), + b"x" => Ok(Unit::X), b"fr" => Ok(Unit::Fr), _ => Err("invalid unit"), } @@ -168,7 +169,8 @@ impl Into for Unit { Unit::Khz => "kHz", Unit::Dpi => "dpi", Unit::Dpcm => "dpcm", - Unit::Dppx | Unit::X => "dppx", + Unit::Dppx => "dppx", + Unit::X => "x", Unit::Fr => "fr", Unit::None => "", } @@ -211,7 +213,8 @@ impl Into<&'static str> for Unit { Unit::Khz => "kHz", Unit::Dpi => "dpi", Unit::Dpcm => "dpcm", - Unit::Dppx | Unit::X => "dppx", + Unit::Dppx => "dppx", + Unit::X => "x", Unit::Fr => "fr", Unit::None => "", } @@ -253,7 +256,8 @@ impl fmt::Display for Unit { Unit::Khz => write!(f, "kHz"), Unit::Dpi => write!(f, "dpi"), Unit::Dpcm => write!(f, "dpcm"), - Unit::Dppx | Unit::X => write!(f, "dppx"), + Unit::Dppx => write!(f, "dppx"), + Unit::X => write!(f, "x"), Unit::Fr => write!(f, "fr"), Unit::None => write!(f, ""), }