use super::util::{opt_spacelike, spacelike2};
use super::value::{numeric, special_function, variable};
use super::{
call_args, ignore_comments, position, sass_string, PResult, Span,
};
use crate::sass::{BinOp, CallArgs, Value};
use crate::value::Operator;
use nom::branch::alt;
use nom::bytes::complete::{tag, tag_no_case};
use nom::character::complete::multispace0;
use nom::combinator::{into, map, not, peek, value};
use nom::sequence::{delimited, pair, preceded, terminated};
use nom::Parser as _;
pub fn css_function(input: Span) -> PResult<Value> {
let (rest, arg) = delimited(
terminated(tag_no_case("calc("), ignore_comments),
one_arg,
preceded(ignore_comments, tag(")")),
)
.parse(input)?;
let pos = input.up_to(&rest).to_owned();
Ok((
rest,
Value::Call("calc".into(), CallArgs::new_single(arg), pos),
))
}
fn one_arg(input: Span) -> PResult<Value> {
alt((
map(delimited(tag("("), one_arg, tag(")")), |v| {
Value::Paren(v.into(), true)
}),
sum_expression,
map(sass_string, Value::Literal),
))
.parse(input)
}
fn sum_expression(input: Span) -> PResult<Value> {
let (mut rest, mut v) = term(input)?;
while let Ok((nrest, (s1, op, s2, v2, end))) = alt((
(
value(false, tag("")),
alt((
value(Operator::Plus, tag("+")),
value(Operator::Minus, tag("-")),
)),
ignore_comments,
term,
position,
),
(
value(true, spacelike2),
alt((
value(Operator::Plus, tag("+")),
value(Operator::Minus, terminated(tag("-"), spacelike2)),
)),
ignore_comments,
term,
position,
),
))
.parse(rest)
{
let pos = input.up_to(&end).to_owned();
v = BinOp::new(v, s1, op, s2, v2, pos).into();
rest = nrest;
}
Ok((rest, v))
}
fn term(input: Span) -> PResult<Value> {
let (mut rest, mut v) = single_value(input)?;
while let Ok((nrest, (s1, op, s2, v2, end))) = (
map(multispace0, |s: Span| !s.fragment().is_empty()),
alt((
value(Operator::Multiply, tag("*")),
value(Operator::Div, terminated(tag("/"), peek(not(tag("/"))))),
value(Operator::Modulo, tag("%")),
)),
map(multispace0, |s: Span| !s.fragment().is_empty()),
single_value,
position,
)
.parse(rest)
{
let pos = input.up_to(&end).to_owned();
rest = nrest;
v = BinOp::new(v, s1, op, s2, v2, pos).into();
}
Ok((rest, v))
}
fn single_value(input: Span) -> PResult<Value> {
alt((
paren,
value(Value::True, tag("true")),
value(Value::False, tag("false")),
value(Value::HereSelector, tag("&")),
into(numeric),
variable,
value(Value::Null, tag("null")),
special_function,
function_call,
))
.parse(input)
}
fn paren(input: Span) -> PResult<Value> {
map(
delimited(
terminated(tag("("), opt_spacelike),
sum_expression,
preceded(opt_spacelike, tag(")")),
),
|inner| Value::Paren(Box::new(inner), false),
)
.parse(input)
}
fn function_call(input: Span) -> PResult<Value> {
let (rest, (name, args)) = pair(sass_string, call_args).parse(input)?;
let pos = input.up_to(&rest).to_owned();
Ok((rest, Value::Call(name, args, pos)))
}