grass/src/args.rs

301 lines
8.7 KiB
Rust
Raw Normal View History

2020-04-02 12:07:54 -04:00
use std::collections::HashMap;
2020-01-25 11:00:29 -05:00
use std::iter::Peekable;
2020-03-29 13:28:17 -04:00
use crate::common::Pos;
use crate::error::SassResult;
use crate::scope::Scope;
2020-03-01 12:03:14 -05:00
use crate::selector::Selector;
use crate::utils::{
2020-04-01 15:32:52 -04:00
devour_whitespace, devour_whitespace_or_comment, eat_ident, read_until_closing_paren,
read_until_closing_quote, read_until_closing_square_brace,
};
use crate::value::Value;
2020-03-29 13:28:17 -04:00
use crate::Token;
2020-01-25 11:00:29 -05:00
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct FuncArgs(pub Vec<FuncArg>);
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct FuncArg {
pub name: String,
pub default: Option<Vec<Token>>,
2020-04-02 12:07:54 -04:00
pub is_variadic: bool,
2020-01-25 11:00:29 -05:00
}
impl FuncArgs {
pub const fn new() -> Self {
FuncArgs(Vec::new())
}
}
#[derive(Debug, Clone)]
pub(crate) struct CallArgs(HashMap<CallArg, Vec<Token>>);
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
enum CallArg {
Named(String),
Positional(usize),
}
2020-01-25 11:00:29 -05:00
2020-04-02 13:33:26 -04:00
impl CallArg {
pub fn position(&self) -> SassResult<usize> {
match self {
Self::Named(..) => todo!(),
Self::Positional(p) => Ok(*p),
}
}
2020-04-04 12:31:43 -04:00
pub fn decrement(self) -> CallArg {
match self {
Self::Named(..) => self,
Self::Positional(p) => Self::Positional(p - 1),
}
}
2020-04-02 13:33:26 -04:00
}
2020-01-25 11:00:29 -05:00
impl CallArgs {
pub fn new() -> Self {
2020-04-02 12:07:54 -04:00
CallArgs(HashMap::new())
2020-01-25 11:00:29 -05:00
}
/// Get argument by name
///
/// Removes the argument
pub fn get_named(
&mut self,
val: String,
scope: &Scope,
super_selector: &Selector,
) -> Option<SassResult<Value>> {
match self.0.remove(&CallArg::Named(val)) {
2020-04-06 00:11:18 -04:00
Some(v) => Some(Value::from_vec(v, scope, super_selector)),
None => None,
}
}
/// Get a positional argument by 0-indexed position
///
/// Removes the argument
pub fn get_positional(
&mut self,
val: usize,
scope: &Scope,
super_selector: &Selector,
) -> Option<SassResult<Value>> {
match self.0.remove(&CallArg::Positional(val)) {
2020-04-06 00:11:18 -04:00
Some(v) => Some(Value::from_vec(v, scope, super_selector)),
None => None,
}
2020-01-25 11:00:29 -05:00
}
pub fn get_variadic(self, scope: &Scope, super_selector: &Selector) -> SassResult<Vec<Value>> {
2020-04-02 13:33:26 -04:00
let mut vals = Vec::new();
let mut args = self
.0
.into_iter()
.map(|(a, v)| Ok((a.position()?, v)))
.collect::<SassResult<Vec<(usize, Vec<Token>)>>>()?;
2020-04-02 13:33:26 -04:00
args.sort_by(|(a1, _), (a2, _)| a1.cmp(a2));
for arg in args {
2020-04-06 00:11:18 -04:00
vals.push(Value::from_vec(arg.1, scope, super_selector)?);
2020-04-02 13:33:26 -04:00
}
2020-04-02 13:45:14 -04:00
Ok(vals)
2020-04-02 13:33:26 -04:00
}
2020-04-04 12:31:43 -04:00
pub fn decrement(self) -> Self {
CallArgs(
self.0
.into_iter()
.map(|(k, v)| (k.decrement(), v))
.collect(),
)
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.len() == 0
}
2020-01-25 11:00:29 -05:00
}
pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
2020-03-01 12:03:14 -05:00
super_selector: &Selector,
2020-02-17 09:28:25 -05:00
) -> SassResult<FuncArgs> {
2020-01-25 11:00:29 -05:00
let mut args: Vec<FuncArg> = Vec::new();
devour_whitespace(toks);
while let Some(Token { kind, .. }) = toks.next() {
let name = match kind {
2020-03-29 13:28:17 -04:00
'$' => eat_ident(toks, scope, super_selector)?,
')' => break,
2020-01-25 11:00:29 -05:00
_ => todo!(),
};
let mut default: Vec<Token> = Vec::new();
2020-04-02 12:07:54 -04:00
let mut is_variadic = false;
2020-01-25 11:00:29 -05:00
devour_whitespace(toks);
let kind = match toks.next() {
Some(Token { kind, .. }) => kind,
_ => todo!("unexpected eof"),
};
match kind {
2020-03-29 13:28:17 -04:00
':' => {
2020-01-25 11:00:29 -05:00
devour_whitespace(toks);
while let Some(tok) = toks.peek() {
match &tok.kind {
2020-03-29 13:28:17 -04:00
',' => {
2020-01-25 11:00:29 -05:00
toks.next();
args.push(FuncArg {
2020-03-31 01:22:41 -04:00
name: name.replace('_', "-"),
default: Some(default),
2020-04-02 12:07:54 -04:00
is_variadic,
2020-01-25 11:00:29 -05:00
});
break;
}
2020-03-29 13:28:17 -04:00
')' => {
2020-01-25 11:00:29 -05:00
args.push(FuncArg {
2020-03-31 01:22:41 -04:00
name: name.replace('_', "-"),
default: Some(default),
2020-04-02 12:07:54 -04:00
is_variadic,
2020-01-25 11:00:29 -05:00
});
break;
}
_ => {
let tok = toks.next().expect("we know this exists!");
default.push(tok)
}
}
}
}
2020-04-02 12:07:54 -04:00
'.' => {
if toks.next().ok_or("expected \".\".")?.kind != '.' {
return Err("expected \".\".".into());
}
if toks.next().ok_or("expected \".\".")?.kind != '.' {
return Err("expected \".\".".into());
}
devour_whitespace(toks);
if toks.next().ok_or("expected \")\".")?.kind != ')' {
return Err("expected \")\".".into());
}
is_variadic = true;
args.push(FuncArg {
name: name.replace('_', "-"),
default: Some(default),
2020-04-02 12:07:54 -04:00
is_variadic,
});
break;
}
2020-03-29 13:28:17 -04:00
')' => {
2020-01-25 11:00:29 -05:00
args.push(FuncArg {
2020-03-31 01:22:41 -04:00
name: name.replace('_', "-"),
2020-01-25 11:00:29 -05:00
default: if default.is_empty() {
None
} else {
Some(default)
2020-01-25 11:00:29 -05:00
},
2020-04-02 12:07:54 -04:00
is_variadic,
2020-01-25 11:00:29 -05:00
});
break;
}
2020-03-29 13:28:17 -04:00
',' => args.push(FuncArg {
2020-03-31 01:22:41 -04:00
name: name.replace('_', "-"),
2020-01-25 11:00:29 -05:00
default: None,
2020-04-02 12:07:54 -04:00
is_variadic,
2020-01-25 11:00:29 -05:00
}),
_ => {}
}
devour_whitespace(toks);
}
devour_whitespace(toks);
2020-03-29 13:28:17 -04:00
if let Some(Token { kind: '{', .. }) = toks.next() {
2020-01-25 11:00:29 -05:00
} else {
2020-01-25 12:46:51 -05:00
todo!("expected `{{` after args")
2020-01-25 11:00:29 -05:00
}
2020-02-17 09:28:25 -05:00
Ok(FuncArgs(args))
2020-01-25 11:00:29 -05:00
}
pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
2020-03-01 12:03:14 -05:00
super_selector: &Selector,
) -> SassResult<CallArgs> {
let mut args: HashMap<CallArg, Vec<Token>> = HashMap::new();
2020-03-29 13:28:17 -04:00
devour_whitespace_or_comment(toks)?;
let mut name = String::new();
let mut val: Vec<Token> = Vec::new();
loop {
match toks.peek().unwrap().kind {
2020-03-29 13:28:17 -04:00
'$' => {
toks.next();
let v = eat_ident(toks, scope, super_selector)?;
devour_whitespace_or_comment(toks)?;
if toks.peek().unwrap().kind == ':' {
toks.next();
2020-03-29 13:28:17 -04:00
name = v;
} else {
2020-03-29 13:28:17 -04:00
val.push(Token::new(Pos::new(), '$'));
val.extend(v.chars().map(|x| Token::new(Pos::new(), x)));
name.clear();
}
}
2020-03-29 13:28:17 -04:00
')' => {
toks.next();
return Ok(CallArgs(args));
2020-01-25 11:00:29 -05:00
}
_ => name.clear(),
}
2020-03-29 13:28:17 -04:00
devour_whitespace_or_comment(toks)?;
while let Some(tok) = toks.next() {
match tok.kind {
2020-03-29 13:28:17 -04:00
')' => {
args.insert(
if name.is_empty() {
CallArg::Positional(args.len())
} else {
CallArg::Named(name.replace('_', "-"))
},
val,
);
return Ok(CallArgs(args));
}
2020-03-29 13:28:17 -04:00
',' => break,
'[' => {
val.push(tok);
2020-04-01 15:32:52 -04:00
val.extend(read_until_closing_square_brace(toks));
}
2020-03-29 13:28:17 -04:00
'(' => {
2020-02-09 14:27:54 -05:00
val.push(tok);
2020-04-01 15:32:52 -04:00
val.extend(read_until_closing_paren(toks));
2020-02-09 14:27:54 -05:00
}
'"' | '\'' => {
val.push(tok);
val.extend(read_until_closing_quote(toks, tok.kind));
}
_ => val.push(tok),
2020-02-09 14:27:54 -05:00
}
}
args.insert(
if name.is_empty() {
CallArg::Positional(args.len())
} else {
CallArg::Named(name.replace('_', "-"))
},
val.clone(),
);
val.clear();
2020-03-29 13:28:17 -04:00
devour_whitespace(toks);
if toks.peek().is_none() {
return Ok(CallArgs(args));
}
}
}