diff --git a/src/args.rs b/src/args.rs index 779da8a..25ac36d 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,8 +1,9 @@ use std::collections::BTreeMap; use std::iter::Peekable; -use crate::common::Symbol; +use crate::common::{Scope, Symbol}; use crate::utils::devour_whitespace; +use crate::value::Value; use crate::{Token, TokenKind}; #[derive(Debug, Clone, Eq, PartialEq)] @@ -11,7 +12,7 @@ pub(crate) struct FuncArgs(pub Vec); #[derive(Debug, Clone, Eq, PartialEq)] pub(crate) struct FuncArg { pub name: String, - pub default: Option>, + pub default: Option, } impl FuncArgs { @@ -21,7 +22,7 @@ impl FuncArgs { } #[derive(Debug, Clone, std::default::Default)] -pub(crate) struct CallArgs(pub BTreeMap>); +pub(crate) struct CallArgs(pub BTreeMap); impl CallArgs { pub fn new() -> Self { @@ -32,12 +33,15 @@ impl CallArgs { self.0.is_empty() } - pub fn get(&self, val: &str) -> Option<&Vec> { + pub fn get(&self, val: &str) -> Option<&Value> { self.0.get(val) } } -pub(crate) fn eat_func_args>(toks: &mut Peekable) -> FuncArgs { +pub(crate) fn eat_func_args>( + toks: &mut Peekable, + scope: &Scope, +) -> FuncArgs { let mut args: Vec = Vec::new(); devour_whitespace(toks); @@ -62,14 +66,20 @@ pub(crate) fn eat_func_args>(toks: &mut Peekable) - toks.next(); args.push(FuncArg { name, - default: Some(default), + default: Some( + Value::from_tokens(&mut default.into_iter().peekable(), scope) + .unwrap(), + ), }); break; } TokenKind::Symbol(Symbol::CloseParen) => { args.push(FuncArg { name, - default: Some(default), + default: Some( + Value::from_tokens(&mut default.into_iter().peekable(), scope) + .unwrap(), + ), }); break; } @@ -87,7 +97,9 @@ pub(crate) fn eat_func_args>(toks: &mut Peekable) - default: if default.is_empty() { None } else { - Some(default) + Some( + Value::from_tokens(&mut default.into_iter().peekable(), scope).unwrap(), + ) }, }); break; @@ -112,21 +124,70 @@ pub(crate) fn eat_func_args>(toks: &mut Peekable) - FuncArgs(args) } -pub(crate) fn eat_call_args>(toks: &mut Peekable) -> CallArgs { - let mut args: BTreeMap> = BTreeMap::new(); +pub(crate) fn eat_call_args>( + toks: &mut Peekable, + scope: &Scope, +) -> CallArgs { + let mut args: BTreeMap = BTreeMap::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::Variable(v) => { + devour_whitespace(toks); + match toks.peek() { + Some(Token { + kind: TokenKind::Symbol(Symbol::Colon), + .. + }) => name = Some(v), + Some(Token { + kind: TokenKind::Symbol(Symbol::Comma), + .. + }) => { + toks.next(); + match name { + Some(ref name) => { + args.insert(name.clone(), scope.vars.get(&v).unwrap().clone()) + } + None => args.insert( + format!("{}", args.len()), + scope.vars.get(&v).unwrap().clone(), + ), + }; + if let Some(ref mut s) = name { + s.clear(); + } + val.clear(); + } + Some(Token { + kind: TokenKind::Symbol(Symbol::CloseParen), + .. + }) => { + toks.next(); + match name { + Some(name) => args.insert(name, scope.vars.get(&v).unwrap().clone()), + None => args.insert( + format!("{}", args.len()), + scope.vars.get(&v).unwrap().clone(), + ), + }; + break; + } + _ => todo!("unexpected token after variable in call args"), + } + } TokenKind::Symbol(Symbol::Colon) => { devour_whitespace(toks); while let Some(tok) = toks.peek() { match &tok.kind { TokenKind::Symbol(Symbol::Comma) => { toks.next(); - args.insert(name.clone().unwrap(), val.clone()); + args.insert( + name.clone().unwrap(), + Value::from_tokens(&mut val.clone().into_iter().peekable(), scope) + .unwrap(), + ); if let Some(ref mut s) = name { s.clear(); } @@ -134,7 +195,11 @@ pub(crate) fn eat_call_args>(toks: &mut Peekable) - break; } TokenKind::Symbol(Symbol::CloseParen) => { - args.insert(name.clone().unwrap(), val.clone()); + args.insert( + name.clone().unwrap(), + Value::from_tokens(&mut val.clone().into_iter().peekable(), scope) + .unwrap(), + ); break; } _ => val.push(toks.next().expect("we know this exists!")), @@ -143,15 +208,27 @@ pub(crate) fn eat_call_args>(toks: &mut Peekable) - } TokenKind::Symbol(Symbol::CloseParen) => { match name { - Some(name) => args.insert(name, val), - None => args.insert(format!("{}", args.len()), val), + Some(name) => args.insert( + name, + Value::from_tokens(&mut val.into_iter().peekable(), scope).unwrap(), + ), + None => args.insert( + format!("{}", args.len()), + Value::from_tokens(&mut val.into_iter().peekable(), scope).unwrap(), + ), }; break; } TokenKind::Symbol(Symbol::Comma) => { match name { - Some(ref name) => args.insert(name.clone(), val.clone()), - None => args.insert(format!("{}", args.len()), val.clone()), + Some(ref name) => args.insert( + name.clone(), + Value::from_tokens(&mut val.clone().into_iter().peekable(), scope).unwrap(), + ), + None => args.insert( + format!("{}", args.len()), + Value::from_tokens(&mut val.clone().into_iter().peekable(), scope).unwrap(), + ), }; if let Some(ref mut s) = name { s.clear(); diff --git a/src/common.rs b/src/common.rs index a2b3cf6..97fab5b 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,11 +1,10 @@ use std::collections::HashMap; use std::convert::TryFrom; -use std::default::Default; use std::fmt::{self, Display}; use crate::function::Function; use crate::mixin::Mixin; -use crate::Token; +use crate::value::Value; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Symbol { @@ -337,9 +336,9 @@ impl Display for Pos { } } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub(crate) struct Scope { - pub vars: HashMap>, + pub vars: HashMap, pub mixins: HashMap, pub functions: HashMap, } diff --git a/src/function.rs b/src/function.rs index 6489860..d955186 100644 --- a/src/function.rs +++ b/src/function.rs @@ -42,7 +42,7 @@ impl Function { Some(Token { kind: TokenKind::Symbol(Symbol::OpenParen), .. - }) => eat_func_args(toks), + }) => eat_func_args(toks, scope), _ => return Err((pos, String::from("expected `(` after function declaration"))), }; diff --git a/src/lib.rs b/src/lib.rs index 8b46a66..061ead6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,7 @@ use crate::mixin::{eat_include, Mixin}; use crate::selector::{Attribute, Selector}; use crate::style::Style; use crate::utils::{devour_whitespace, eat_variable_value, IsComment, IsWhitespace}; +use crate::value::Value; mod args; mod atrule; @@ -200,7 +201,7 @@ enum Expr { /// A full selector `a > h1` Selector(Selector), /// A variable declaration `$var: 1px` - VariableDecl(String, Vec), + VariableDecl(String, Value), /// A mixin declaration `@mixin foo {}` MixinDecl(String, Mixin), FunctionDecl(String, Function), diff --git a/src/mixin.rs b/src/mixin.rs index afda5f1..8d78418 100644 --- a/src/mixin.rs +++ b/src/mixin.rs @@ -42,7 +42,7 @@ impl Mixin { Some(Token { kind: TokenKind::Symbol(Symbol::OpenParen), .. - }) => eat_func_args(toks), + }) => eat_func_args(toks, scope), Some(Token { kind: TokenKind::Symbol(Symbol::OpenCurlyBrace), .. @@ -137,7 +137,7 @@ pub(crate) fn eat_include>( let args = if let Some(tok) = toks.next() { match tok.kind { TokenKind::Symbol(Symbol::SemiColon) => CallArgs::new(), - TokenKind::Symbol(Symbol::OpenParen) => eat_call_args(toks), + TokenKind::Symbol(Symbol::OpenParen) => eat_call_args(toks, scope), _ => return Err((pos, String::from("expected `(` or `;`"))), } } else {