2020-06-16 20:00:11 -04:00
|
|
|
use std::{collections::HashMap, mem};
|
2020-06-16 19:38:30 -04:00
|
|
|
|
|
|
|
use codemap::{Span, Spanned};
|
|
|
|
|
2020-06-16 20:00:11 -04:00
|
|
|
use crate::{
|
|
|
|
args::{CallArg, CallArgs, FuncArg, FuncArgs},
|
|
|
|
error::SassResult,
|
2020-07-02 10:37:13 -04:00
|
|
|
scope::Scope,
|
2020-07-25 14:10:57 -04:00
|
|
|
utils::{
|
2020-07-25 15:03:45 -04:00
|
|
|
peek_ident_no_interpolation, peek_whitespace_or_comment, read_until_closing_paren,
|
|
|
|
read_until_closing_quote, read_until_closing_square_brace,
|
2020-07-25 14:10:57 -04:00
|
|
|
},
|
2020-06-16 20:00:11 -04:00
|
|
|
value::Value,
|
|
|
|
Token,
|
2020-06-16 19:38:30 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
use super::Parser;
|
|
|
|
|
|
|
|
impl<'a> Parser<'a> {
|
|
|
|
pub(super) fn parse_func_args(&mut self) -> SassResult<FuncArgs> {
|
|
|
|
let mut args: Vec<FuncArg> = Vec::new();
|
2020-06-18 03:09:24 -04:00
|
|
|
let mut close_paren_span: Span = match self.toks.peek() {
|
|
|
|
Some(Token { pos, .. }) => *pos,
|
|
|
|
None => return Err(("expected \")\".", self.span_before).into()),
|
|
|
|
};
|
2020-06-16 19:38:30 -04:00
|
|
|
|
2020-07-25 15:03:45 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-06-16 19:38:30 -04:00
|
|
|
while let Some(Token { kind, pos }) = self.toks.next() {
|
|
|
|
let name = match kind {
|
|
|
|
'$' => self.parse_identifier_no_interpolation(false)?,
|
|
|
|
')' => {
|
|
|
|
close_paren_span = pos;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_ => return Err(("expected \")\".", pos).into()),
|
|
|
|
};
|
|
|
|
let mut default: Vec<Token> = Vec::new();
|
|
|
|
let mut is_variadic = false;
|
2020-07-25 15:03:45 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-06-16 19:38:30 -04:00
|
|
|
let (kind, span) = match self.toks.next() {
|
|
|
|
Some(Token { kind, pos }) => (kind, pos),
|
|
|
|
None => return Err(("expected \")\".", pos).into()),
|
|
|
|
};
|
|
|
|
match kind {
|
|
|
|
':' => {
|
2020-07-25 15:03:45 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-06-16 19:38:30 -04:00
|
|
|
while let Some(tok) = self.toks.peek() {
|
|
|
|
match &tok.kind {
|
|
|
|
',' => {
|
|
|
|
self.toks.next();
|
2020-07-27 22:09:38 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-06-16 19:38:30 -04:00
|
|
|
args.push(FuncArg {
|
|
|
|
name: name.node.into(),
|
|
|
|
default: Some(default),
|
|
|
|
is_variadic,
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
')' => {
|
|
|
|
args.push(FuncArg {
|
|
|
|
name: name.node.into(),
|
|
|
|
default: Some(default),
|
|
|
|
is_variadic,
|
|
|
|
});
|
|
|
|
close_paren_span = tok.pos();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
'(' => {
|
|
|
|
default.push(self.toks.next().unwrap());
|
|
|
|
default.extend(read_until_closing_paren(self.toks)?);
|
|
|
|
}
|
|
|
|
_ => default.push(self.toks.next().unwrap()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'.' => {
|
|
|
|
let next = self.toks.next().ok_or(("expected \".\".", span))?;
|
|
|
|
if next.kind != '.' {
|
|
|
|
return Err(("expected \".\".", next.pos()).into());
|
|
|
|
}
|
|
|
|
let next = self.toks.next().ok_or(("expected \".\".", next.pos()))?;
|
|
|
|
if next.kind != '.' {
|
|
|
|
return Err(("expected \".\".", next.pos()).into());
|
|
|
|
}
|
2020-07-25 15:03:45 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-06-16 19:38:30 -04:00
|
|
|
let next = self.toks.next().ok_or(("expected \")\".", next.pos()))?;
|
|
|
|
if next.kind != ')' {
|
|
|
|
return Err(("expected \")\".", next.pos()).into());
|
|
|
|
}
|
|
|
|
|
|
|
|
is_variadic = true;
|
|
|
|
|
|
|
|
args.push(FuncArg {
|
|
|
|
name: name.node.into(),
|
|
|
|
default: Some(default),
|
|
|
|
is_variadic,
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
')' => {
|
|
|
|
close_paren_span = span;
|
|
|
|
args.push(FuncArg {
|
|
|
|
name: name.node.into(),
|
|
|
|
default: if default.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(default)
|
|
|
|
},
|
|
|
|
is_variadic,
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
',' => args.push(FuncArg {
|
|
|
|
name: name.node.into(),
|
|
|
|
default: None,
|
|
|
|
is_variadic,
|
|
|
|
}),
|
|
|
|
_ => {}
|
|
|
|
}
|
2020-07-25 15:03:45 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-06-16 19:38:30 -04:00
|
|
|
}
|
2020-07-25 15:03:45 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-07-02 10:31:32 -04:00
|
|
|
// TODO: this should NOT eat the opening curly brace
|
2020-06-16 19:38:30 -04:00
|
|
|
match self.toks.next() {
|
|
|
|
Some(v) if v.kind == '{' => {}
|
|
|
|
Some(..) | None => return Err(("expected \"{\".", close_paren_span).into()),
|
|
|
|
};
|
|
|
|
Ok(FuncArgs(args))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn parse_call_args(&mut self) -> SassResult<CallArgs> {
|
2020-07-02 16:32:43 -04:00
|
|
|
let mut args = HashMap::new();
|
2020-06-16 19:38:30 -04:00
|
|
|
self.whitespace_or_comment();
|
|
|
|
let mut name = String::new();
|
|
|
|
let mut val: Vec<Token> = Vec::new();
|
|
|
|
let mut span = self
|
|
|
|
.toks
|
|
|
|
.peek()
|
|
|
|
.ok_or(("expected \")\".", self.span_before))?
|
|
|
|
.pos();
|
|
|
|
loop {
|
2020-06-18 03:09:24 -04:00
|
|
|
match self.toks.peek().cloned() {
|
|
|
|
Some(Token { kind: '$', pos }) => {
|
|
|
|
span = span.merge(pos);
|
|
|
|
self.toks.next();
|
2020-07-25 14:10:57 -04:00
|
|
|
let v = peek_ident_no_interpolation(self.toks, false, self.span_before)?;
|
|
|
|
|
2020-07-25 15:03:45 -04:00
|
|
|
peek_whitespace_or_comment(self.toks);
|
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
if let Some(Token { kind: ':', .. }) = self.toks.peek() {
|
2020-07-25 14:10:57 -04:00
|
|
|
self.toks.truncate_iterator_to_cursor();
|
2020-06-16 19:38:30 -04:00
|
|
|
self.toks.next();
|
|
|
|
name = v.node;
|
|
|
|
} else {
|
|
|
|
val.push(Token::new(pos, '$'));
|
2020-07-25 14:10:57 -04:00
|
|
|
self.toks.reset_cursor();
|
2020-06-16 19:38:30 -04:00
|
|
|
name.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(Token { kind: ')', .. }) => {
|
|
|
|
self.toks.next();
|
|
|
|
return Ok(CallArgs(args, span));
|
|
|
|
}
|
|
|
|
Some(..) | None => name.clear(),
|
|
|
|
}
|
|
|
|
self.whitespace_or_comment();
|
|
|
|
|
2020-07-02 17:25:52 -04:00
|
|
|
let mut is_splat = false;
|
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
while let Some(tok) = self.toks.next() {
|
|
|
|
match tok.kind {
|
|
|
|
')' => {
|
|
|
|
args.insert(
|
|
|
|
if name.is_empty() {
|
|
|
|
CallArg::Positional(args.len())
|
|
|
|
} else {
|
2020-07-08 10:38:25 -04:00
|
|
|
CallArg::Named(mem::take(&mut name).into())
|
2020-06-16 19:38:30 -04:00
|
|
|
},
|
2020-07-09 23:52:35 -04:00
|
|
|
self.parse_value_from_vec(val, true),
|
2020-06-16 19:38:30 -04:00
|
|
|
);
|
|
|
|
span = span.merge(tok.pos());
|
|
|
|
return Ok(CallArgs(args, span));
|
|
|
|
}
|
|
|
|
',' => break,
|
|
|
|
'[' => {
|
|
|
|
val.push(tok);
|
2020-07-08 19:25:35 -04:00
|
|
|
val.append(&mut read_until_closing_square_brace(self.toks)?);
|
2020-06-16 19:38:30 -04:00
|
|
|
}
|
|
|
|
'(' => {
|
|
|
|
val.push(tok);
|
2020-07-08 19:25:35 -04:00
|
|
|
val.append(&mut read_until_closing_paren(self.toks)?);
|
2020-06-16 19:38:30 -04:00
|
|
|
}
|
|
|
|
'"' | '\'' => {
|
|
|
|
val.push(tok);
|
2020-07-08 19:25:35 -04:00
|
|
|
val.append(&mut read_until_closing_quote(self.toks, tok.kind)?);
|
2020-06-16 19:38:30 -04:00
|
|
|
}
|
2020-07-02 17:25:52 -04:00
|
|
|
'.' => {
|
|
|
|
if let Some(Token { kind: '.', pos }) = self.toks.peek().cloned() {
|
|
|
|
if !name.is_empty() {
|
|
|
|
return Err(("expected \")\".", pos).into());
|
|
|
|
}
|
|
|
|
self.toks.next();
|
|
|
|
if let Some(Token { kind: '.', .. }) = self.toks.peek() {
|
|
|
|
self.toks.next();
|
|
|
|
is_splat = true;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
return Err(("expected \".\".", pos).into());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
val.push(tok);
|
|
|
|
}
|
|
|
|
}
|
2020-06-16 19:38:30 -04:00
|
|
|
_ => val.push(tok),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-02 17:25:52 -04:00
|
|
|
if is_splat {
|
2020-07-09 23:52:35 -04:00
|
|
|
let val = self.parse_value_from_vec(mem::take(&mut val), true)?;
|
2020-07-02 17:25:52 -04:00
|
|
|
match val.node {
|
|
|
|
Value::ArgList(v) => {
|
|
|
|
for arg in v {
|
2020-07-03 15:06:26 -04:00
|
|
|
args.insert(CallArg::Positional(args.len()), Ok(arg));
|
2020-07-02 17:25:52 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Value::List(v, ..) => {
|
|
|
|
for arg in v {
|
2020-07-03 15:06:26 -04:00
|
|
|
args.insert(CallArg::Positional(args.len()), Ok(arg.span(val.span)));
|
2020-07-02 17:25:52 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Value::Map(v) => {
|
2020-07-06 17:54:55 -04:00
|
|
|
// NOTE: we clone the map here because it is used
|
|
|
|
// later for error reporting. perhaps there is
|
|
|
|
// some way around this?
|
|
|
|
for (name, arg) in v.clone().entries() {
|
|
|
|
let name = match name {
|
|
|
|
Value::String(s, ..) => s,
|
|
|
|
_ => {
|
|
|
|
return Err((
|
|
|
|
format!(
|
|
|
|
"{} is not a string in {}.",
|
|
|
|
name.inspect(val.span)?,
|
|
|
|
Value::Map(v).inspect(val.span)?
|
|
|
|
),
|
|
|
|
val.span,
|
|
|
|
)
|
|
|
|
.into())
|
|
|
|
}
|
|
|
|
};
|
2020-07-03 15:06:26 -04:00
|
|
|
args.insert(CallArg::Named(name.into()), Ok(arg.span(val.span)));
|
2020-07-02 17:25:52 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
2020-07-03 15:06:26 -04:00
|
|
|
args.insert(CallArg::Positional(args.len()), Ok(val));
|
2020-07-02 17:25:52 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
args.insert(
|
|
|
|
if name.is_empty() {
|
|
|
|
CallArg::Positional(args.len())
|
|
|
|
} else {
|
2020-07-08 10:38:25 -04:00
|
|
|
CallArg::Named(mem::take(&mut name).into())
|
2020-07-02 17:25:52 -04:00
|
|
|
},
|
2020-07-09 23:52:35 -04:00
|
|
|
self.parse_value_from_vec(mem::take(&mut val), true),
|
2020-07-02 17:25:52 -04:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-07-25 15:03:45 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-06-16 19:38:30 -04:00
|
|
|
|
|
|
|
if self.toks.peek().is_none() {
|
2020-07-07 18:06:28 -04:00
|
|
|
return Err(("expected \")\".", span).into());
|
2020-06-16 19:38:30 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Parser<'a> {
|
2020-07-08 21:31:21 -04:00
|
|
|
pub(super) fn eval_args(&mut self, fn_args: FuncArgs, mut args: CallArgs) -> SassResult<Scope> {
|
2020-07-08 14:51:04 -04:00
|
|
|
let mut scope = Scope::new();
|
2020-07-08 17:52:37 -04:00
|
|
|
if fn_args.0.is_empty() {
|
|
|
|
args.max_args(0)?;
|
|
|
|
return Ok(scope);
|
|
|
|
}
|
2020-07-08 14:51:04 -04:00
|
|
|
self.scopes.enter_new_scope();
|
2020-07-08 21:31:21 -04:00
|
|
|
for (idx, mut arg) in fn_args.0.into_iter().enumerate() {
|
2020-07-02 10:37:13 -04:00
|
|
|
if arg.is_variadic {
|
|
|
|
let span = args.span();
|
2020-07-10 21:42:10 -04:00
|
|
|
let arg_list = Value::ArgList(args.get_variadic()?);
|
2020-07-02 10:37:13 -04:00
|
|
|
scope.insert_var(
|
2020-07-08 22:38:56 -04:00
|
|
|
arg.name,
|
2020-07-02 10:37:13 -04:00
|
|
|
Spanned {
|
|
|
|
node: arg_list,
|
|
|
|
span,
|
|
|
|
},
|
2020-07-03 12:38:20 -04:00
|
|
|
);
|
2020-07-02 10:37:13 -04:00
|
|
|
break;
|
|
|
|
}
|
2020-07-08 22:38:56 -04:00
|
|
|
let val = match args.get(idx, arg.name) {
|
2020-07-02 16:32:43 -04:00
|
|
|
Some(v) => v,
|
2020-07-02 10:37:13 -04:00
|
|
|
None => match arg.default.as_mut() {
|
2020-07-09 23:52:35 -04:00
|
|
|
Some(v) => self.parse_value_from_vec(mem::take(v), true),
|
2020-07-02 10:37:13 -04:00
|
|
|
None => {
|
|
|
|
return Err(
|
|
|
|
(format!("Missing argument ${}.", &arg.name), args.span()).into()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
},
|
2020-07-03 15:06:26 -04:00
|
|
|
}?;
|
2020-07-08 22:38:56 -04:00
|
|
|
self.scopes.insert_var(arg.name, val.clone());
|
2020-07-08 21:31:21 -04:00
|
|
|
scope.insert_var(arg.name, val);
|
2020-07-02 10:37:13 -04:00
|
|
|
}
|
2020-07-08 14:51:04 -04:00
|
|
|
self.scopes.exit_scope();
|
|
|
|
Ok(scope)
|
2020-07-02 10:37:13 -04:00
|
|
|
}
|
2020-06-16 19:38:30 -04:00
|
|
|
}
|