initial implementation of varargs

This commit is contained in:
ConnorSkees 2020-04-02 13:33:26 -04:00
parent f9fc0ed8cb
commit 39031aefff
8 changed files with 65 additions and 10 deletions

View File

@ -37,11 +37,21 @@ enum CallArg {
Positional(usize),
}
impl CallArg {
pub fn position(&self) -> SassResult<usize> {
match self {
Self::Named(..) => todo!(),
Self::Positional(p) => Ok(*p),
}
}
}
impl CallArgs {
pub fn new() -> Self {
CallArgs(HashMap::new())
}
#[allow(dead_code)]
pub fn get_named(&self, val: String) -> Option<&Value> {
self.0.get(&CallArg::Named(val))
}
@ -50,6 +60,20 @@ impl CallArgs {
self.0.get(&CallArg::Positional(val))
}
pub fn get_variadic(self) -> SassResult<Value> {
let mut vals = Vec::new();
let mut args = self
.0
.into_iter()
.map(|(a, v)| Ok((a.position()?, v)))
.collect::<SassResult<Vec<(usize, Value)>>>()?;
args.sort_by(|(a1, _), (a2, _)| a1.cmp(a2));
for arg in args {
vals.push(arg.1);
}
Ok(Value::ArgList(vals))
}
pub fn len(&self) -> usize {
self.0.len()
}

View File

@ -43,8 +43,12 @@ impl Function {
Ok((name, Function::new(scope, args, body)))
}
pub fn args(mut self, args: &mut CallArgs) -> SassResult<Function> {
pub fn args(mut self, mut args: CallArgs) -> SassResult<Function> {
for (idx, arg) in self.args.0.iter().enumerate() {
if arg.is_variadic {
self.scope.insert_var(&arg.name, args.get_variadic()?)?;
break;
}
let val = match args.remove_positional(idx) {
Some(v) => v,
None => match args.remove_named(arg.name.clone()) {

View File

@ -59,8 +59,12 @@ impl Mixin {
self
}
pub fn args(mut self, args: &mut CallArgs) -> SassResult<Mixin> {
pub fn args(mut self, mut args: CallArgs) -> SassResult<Mixin> {
for (idx, arg) in self.args.0.iter().enumerate() {
if arg.is_variadic {
self.scope.insert_var(&arg.name, args.get_variadic()?)?;
break;
}
let val = match args.remove_positional(idx) {
Some(v) => v,
None => match args.remove_named(arg.name.clone()) {
@ -128,7 +132,7 @@ pub(crate) fn eat_include<I: Iterator<Item = Token>>(
let mut has_include = false;
let mut args = if let Some(tok) = toks.next() {
let args = if let Some(tok) = toks.next() {
match tok.kind {
';' => CallArgs::new(),
'(' => {
@ -170,9 +174,6 @@ pub(crate) fn eat_include<I: Iterator<Item = Token>>(
let mixin = scope.get_mixin(&name)?.clone();
let rules = mixin
.args(&mut args)?
.content(content)
.call(super_selector)?;
let rules = mixin.args(args)?.content(content).call(super_selector)?;
Ok(rules)
}

View File

@ -29,6 +29,7 @@ pub(crate) enum Value {
Paren(Box<Value>),
Ident(String, QuoteKind),
Map(SassMap),
ArgList(Vec<Value>),
// Returned by `get-function()`
// Function(String)
}
@ -120,6 +121,15 @@ impl Display for Value {
Self::True => write!(f, "true"),
Self::False => write!(f, "false"),
Self::Null => write!(f, "null"),
Self::ArgList(args) => write!(
f,
"{}",
args.iter()
.filter(|x| !x.is_null())
.map(std::string::ToString::to_string)
.collect::<Vec<String>>()
.join(", "),
),
}
}
}
@ -157,6 +167,7 @@ impl Value {
Self::Dimension(..) => Ok("number"),
Self::List(..) => Ok("list"),
// Self::Function(..) => Ok("function"),
Self::ArgList(..) => Ok("arglist"),
Self::True | Self::False => Ok("bool"),
Self::Null => Ok("null"),
Self::Map(..) => Ok("map"),

View File

@ -14,7 +14,7 @@ impl Add for Value {
}
let precedence = Op::Plus.precedence();
Ok(match self {
Self::Map(..) => todo!(),
Self::ArgList(..) | Self::Map(..) => todo!(),
Self::Important | Self::True | Self::False => match other {
Self::Ident(s, QuoteKind::Double) | Self::Ident(s, QuoteKind::Single) => {
Value::Ident(format!("{}{}", self, s), QuoteKind::Double)
@ -86,7 +86,7 @@ impl Add for Value {
Self::List(..) => Value::Ident(format!("{}{}", s1, other), quotes1),
Self::UnaryOp(..) | Self::BinaryOp(..) => todo!(),
Self::Paren(..) => (Self::Ident(s1, quotes1) + other.eval()?)?,
Self::Map(..) => todo!(),
Self::ArgList(..) | Self::Map(..) => todo!(),
},
Self::List(..) => match other {
Self::Ident(s, q) => Value::Ident(format!("{}{}", self, s), q.normalize()),

View File

@ -406,7 +406,7 @@ impl Value {
};
Ok(IntermediateValue::Value(
func.clone()
.args(&mut eat_call_args(toks, scope, super_selector)?)?
.args(eat_call_args(toks, scope, super_selector)?)?
.call(super_selector, func.body())?,
))
}

View File

@ -15,3 +15,13 @@ error!(
varargs_two_periods,
"@function foo($a..) {\n @return $a;\n}\n", "Error: expected \".\"."
);
test!(
mixin_varargs_are_comma_separated,
"@mixin foo($a...) {\n color: $a;\n}\n\na {\n @include foo(1, 2, 3, 4, 5);\n}\n",
"a {\n color: 1, 2, 3, 4, 5;\n}\n"
);
test!(
function_varargs_are_comma_separated,
"@function foo($a...) {\n @return $a;\n}\n\na {\n color: foo(1, 2, 3, 4, 5);\n}\n",
"a {\n color: 1, 2, 3, 4, 5;\n}\n"
);

View File

@ -164,6 +164,11 @@ test!(
"a {\n color: type-of(hi + bye)\n}\n",
"a {\n color: string;\n}\n"
);
test!(
type_of_arglist,
"@mixin foo($a...) {color: type-of($a);}\na {@include foo(1, 2, 3, 4, 5);}",
"a {\n color: arglist;\n}\n"
);
test!(
unitless_px,
"a {\n color: unitless(1px)\n}\n",