use super::prelude::*;
use crate::{
AngleOrNumber, AttrFunction, EnvFunction, FirstValidFunction, IfFunction, NoneOr, Number, TreeCountingFunction,
Unresolved, VarFunction,
};
use css_parse::SemanticEq;
#[node]
#[derive(Parse, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub enum MathFunction<'a, T> {
CalcFunction(CalcFunction<'a, T>),
MinFunction(MinFunction<'a, T>),
MaxFunction(MaxFunction<'a, T>),
ClampFunction(ClampFunction<'a, T>),
RoundFunction(RoundFunction<'a, T>),
ModFunction(ModFunction<'a, T>),
RemFunction(RemFunction<'a, T>),
SinFunction(SinFunction<'a>),
CosFunction(CosFunction<'a>),
TanFunction(TanFunction<'a>),
AsinFunction(AsinFunction<'a>),
AcosFunction(AcosFunction<'a>),
AtanFunction(AtanFunction<'a>),
Atan2Function(Atan2Function<'a, T>),
PowFunction(PowFunction<'a>),
SqrtFunction(SqrtFunction<'a>),
HypotFunction(HypotFunction<'a, T>),
LogFunction(LogFunction<'a>),
ExpFunction(ExpFunction<'a>),
AbsFunction(AbsFunction<'a, T>),
SignFunction(SignFunction<'a, T>),
}
pub fn is_math_function(atom: CssAtomSet) -> bool {
matches!(
atom,
CssAtomSet::Calc
| CssAtomSet::Min
| CssAtomSet::Max
| CssAtomSet::Clamp
| CssAtomSet::Round
| CssAtomSet::Mod
| CssAtomSet::Rem
| CssAtomSet::Sin
| CssAtomSet::Cos
| CssAtomSet::Tan
| CssAtomSet::Asin
| CssAtomSet::Acos
| CssAtomSet::Atan
| CssAtomSet::Atan2
| CssAtomSet::Pow
| CssAtomSet::Sqrt
| CssAtomSet::Hypot
| CssAtomSet::Log
| CssAtomSet::Exp
| CssAtomSet::Abs
| CssAtomSet::Sign
)
}
impl<'a, T> Peek<'a> for MathFunction<'a, T> {
fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
where
I: Iterator<Item = Cursor> + Clone,
{
<T![Function]>::peek(p, c) && is_math_function(p.to_atom::<CssAtomSet>(c))
}
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct CalcFunction<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Calc)]
pub name: Function,
pub params: CalcSum<'a, T>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct MinFunction<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Min)]
pub name: Function,
pub params: CommaSeparated<'a, CalcSum<'a, T>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct MaxFunction<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Max)]
pub name: Function,
pub params: CommaSeparated<'a, CalcSum<'a, T>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct ClampFunction<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Clamp)]
pub name: Function,
pub min: NoneOr<CalcSum<'a, T>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub comma_1: Comma,
pub value: Box<'a, CalcSum<'a, T>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub comma_2: Comma,
pub max: NoneOr<CalcSum<'a, T>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct RoundFunction<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Round)]
pub name: Function,
pub strategy: Option<RoundingStrategy>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub comma_1: Option<Comma>,
pub value: Box<'a, CalcSum<'a, T>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub comma_2: Option<Comma>,
pub step: Option<Box<'a, CalcSum<'a, T>>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct ModFunction<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Mod)]
pub name: Function,
pub dividend: CalcSum<'a, T>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub comma: Comma,
pub divisor: Box<'a, CalcSum<'a, T>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct RemFunction<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Rem)]
pub name: Function,
pub dividend: CalcSum<'a, T>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub comma: Comma,
pub divisor: Box<'a, CalcSum<'a, T>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct SinFunction<'a> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Sin)]
pub name: Function,
pub params: CalcSum<'a, AngleOrNumber>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct CosFunction<'a> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Cos)]
pub name: Function,
pub params: CalcSum<'a, AngleOrNumber>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct TanFunction<'a> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Tan)]
pub name: Function,
pub params: CalcSum<'a, AngleOrNumber>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct AsinFunction<'a> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Asin)]
pub name: Function,
pub params: CalcSum<'a, Number>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct AcosFunction<'a> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Acos)]
pub name: Function,
pub params: CalcSum<'a, Number>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct AtanFunction<'a> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Atan)]
pub name: Function,
pub params: CalcSum<'a, Number>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct Atan2Function<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Atan2)]
pub name: Function,
pub y: CalcSum<'a, T>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub comma: Comma,
pub x: Box<'a, CalcSum<'a, T>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct PowFunction<'a> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Pow)]
pub name: Function,
pub base: CalcSum<'a, Number>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub comma: Comma,
pub exponent: Box<'a, CalcSum<'a, Number>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct SqrtFunction<'a> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Sqrt)]
pub name: Function,
pub params: CalcSum<'a, Number>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct HypotFunction<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Hypot)]
pub name: Function,
pub params: CommaSeparated<'a, CalcSum<'a, T>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct LogFunction<'a> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Log)]
pub name: Function,
pub value: CalcSum<'a, Number>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub comma: Option<Comma>,
pub base: Option<Box<'a, CalcSum<'a, Number>>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct ExpFunction<'a> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Exp)]
pub name: Function,
pub params: CalcSum<'a, Number>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct AbsFunction<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Abs)]
pub name: Function,
pub params: CalcSum<'a, T>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub struct SignFunction<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
#[atom(CssAtomSet::Sign)]
pub name: Function,
pub params: CalcSum<'a, T>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
pub struct CalcSum<'a, T> {
pub first: CalcProduct<'a, T>,
pub rest: Vec<'a, (CalcSumOperator, CalcProduct<'a, T>)>,
}
#[node]
#[derive(ToCursors, ToSpan, SemanticEq, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum CalcSumOperator {
Add(T![+]),
Subtract(T![-]),
}
impl<'a> Peek<'a> for CalcSumOperator {
fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
where
I: Iterator<Item = Cursor> + Clone,
{
c == Kind::Delim && (c == '+' || c == '-') && p.peek_n_including_whitespace(1) == Kind::Whitespace
}
}
impl<'a> Parse<'a> for CalcSumOperator {
fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
where
I: Iterator<Item = Cursor> + Clone,
{
let c = p.peek_n(1);
let rules = AssociatedWhitespaceRules::EnforceBefore | AssociatedWhitespaceRules::EnforceAfter;
let op = if c == '+' {
Self::Add(p.parse::<T![+]>()?.with_associated_whitespace(rules))
} else {
Self::Subtract(p.parse::<T![-]>()?.with_associated_whitespace(rules))
};
if p.peek_n_including_whitespace(1) != Kind::Whitespace {
Err(Diagnostic::new(p.peek_n(1), Diagnostic::unexpected_delim))?;
}
Ok(op)
}
}
#[node]
#[derive(Peek, ToCursors, ToSpan, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub enum CalcOperand<'a, T> {
Literal(CalcValue<'a, T>),
Substituted(Box<'a, CalcOperandSubstitutionFunction<'a, T>>),
#[peek(skip)]
Unresolved(Box<'a, Unresolved<'a>>),
}
crate::values::impl_value_slot_parse!(CalcOperand, CalcOperandSubstitutionFunction, CalcValue<T>);
impl<'a, T: SemanticEq> SemanticEq for CalcOperand<'a, T> {
fn semantic_eq(&self, other: &Self) -> bool {
calc_operand_eq(self, other)
}
}
fn calc_operand_eq<'a, T: SemanticEq>(a: &CalcOperand<'a, T>, b: &CalcOperand<'a, T>) -> bool {
match (a, b) {
(CalcOperand::Literal(a), CalcOperand::Literal(b)) => calc_value_eq(a, b),
(CalcOperand::Substituted(a), CalcOperand::Substituted(b)) => calc_operand_substitution_eq(a, b),
(CalcOperand::Unresolved(a), CalcOperand::Unresolved(b)) => a.semantic_eq(b),
_ => false,
}
}
fn calc_value_eq<'a, T: SemanticEq>(a: &CalcValue<'a, T>, b: &CalcValue<'a, T>) -> bool {
match (a, b) {
(CalcValue::Number(a), CalcValue::Number(b)) => a.semantic_eq(b),
(CalcValue::Typed(a), CalcValue::Typed(b)) => a.semantic_eq(b),
(CalcValue::Keyword(a), CalcValue::Keyword(b)) => a.semantic_eq(b),
(CalcValue::TreeCounting(a), CalcValue::TreeCounting(b)) => a.semantic_eq(b),
(CalcValue::Parenthesized(a), CalcValue::Parenthesized(b)) => calc_in_parens_eq(a, b),
_ => false,
}
}
fn calc_in_parens_eq<'a, T: SemanticEq>(a: &CalcInParens<'a, T>, b: &CalcInParens<'a, T>) -> bool {
a.open.semantic_eq(&b.open) && calc_sum_eq(&a.sum, &b.sum)
}
fn calc_sum_eq<'a, T: SemanticEq>(a: &CalcSum<'a, T>, b: &CalcSum<'a, T>) -> bool {
if a.rest.len() != b.rest.len() {
return false;
}
calc_product_eq(&a.first, &b.first)
&& a.rest.iter().zip(b.rest.iter()).all(|((op_a, p_a), (op_b, p_b))| op_a == op_b && calc_product_eq(p_a, p_b))
}
fn calc_product_eq<'a, T: SemanticEq>(a: &CalcProduct<'a, T>, b: &CalcProduct<'a, T>) -> bool {
if a.rest.len() != b.rest.len() {
return false;
}
calc_operand_eq(&a.first, &b.first)
&& a.rest.iter().zip(b.rest.iter()).all(|((op_a, p_a), (op_b, p_b))| op_a == op_b && calc_operand_eq(p_a, p_b))
}
fn calc_operand_substitution_eq<'a, T: SemanticEq>(
a: &CalcOperandSubstitutionFunction<'a, T>,
b: &CalcOperandSubstitutionFunction<'a, T>,
) -> bool {
match (a, b) {
(CalcOperandSubstitutionFunction::Math(a), CalcOperandSubstitutionFunction::Math(b)) => math_function_eq(a, b),
(CalcOperandSubstitutionFunction::Var(a), CalcOperandSubstitutionFunction::Var(b)) => a.semantic_eq(b),
(CalcOperandSubstitutionFunction::Env(a), CalcOperandSubstitutionFunction::Env(b)) => a.semantic_eq(b),
(CalcOperandSubstitutionFunction::Attr(a), CalcOperandSubstitutionFunction::Attr(b)) => a.semantic_eq(b),
(CalcOperandSubstitutionFunction::If(a), CalcOperandSubstitutionFunction::If(b)) => a.semantic_eq(b),
(CalcOperandSubstitutionFunction::FirstValid(a), CalcOperandSubstitutionFunction::FirstValid(b)) => {
a.semantic_eq(b)
}
_ => false,
}
}
fn math_function_eq<'a, T: SemanticEq>(a: &MathFunction<'a, T>, b: &MathFunction<'a, T>) -> bool {
use MathFunction::*;
match (a, b) {
(CalcFunction(a), CalcFunction(b)) => calc_sum_eq(&a.params, &b.params),
(MinFunction(a), MinFunction(b)) => a.params.semantic_eq(&b.params),
(MaxFunction(a), MaxFunction(b)) => a.params.semantic_eq(&b.params),
(ClampFunction(a), ClampFunction(b)) => {
calc_sum_or_none_eq(&a.min, &b.min)
&& calc_sum_eq(&a.value, &b.value)
&& calc_sum_or_none_eq(&a.max, &b.max)
}
(RoundFunction(a), RoundFunction(b)) => {
a.strategy == b.strategy && calc_sum_eq(&a.value, &b.value) && calc_sum_opt_eq(&a.step, &b.step)
}
(ModFunction(a), ModFunction(b)) => {
calc_sum_eq(&a.dividend, &b.dividend) && calc_sum_eq(&a.divisor, &b.divisor)
}
(RemFunction(a), RemFunction(b)) => {
calc_sum_eq(&a.dividend, &b.dividend) && calc_sum_eq(&a.divisor, &b.divisor)
}
(SinFunction(a), SinFunction(b)) => a.semantic_eq(b),
(CosFunction(a), CosFunction(b)) => a.semantic_eq(b),
(TanFunction(a), TanFunction(b)) => a.semantic_eq(b),
(AsinFunction(a), AsinFunction(b)) => a.semantic_eq(b),
(AcosFunction(a), AcosFunction(b)) => a.semantic_eq(b),
(AtanFunction(a), AtanFunction(b)) => a.semantic_eq(b),
(Atan2Function(a), Atan2Function(b)) => a.semantic_eq(b),
(PowFunction(a), PowFunction(b)) => a.semantic_eq(b),
(SqrtFunction(a), SqrtFunction(b)) => a.semantic_eq(b),
(HypotFunction(a), HypotFunction(b)) => a.semantic_eq(b),
(LogFunction(a), LogFunction(b)) => a.semantic_eq(b),
(ExpFunction(a), ExpFunction(b)) => a.semantic_eq(b),
(AbsFunction(a), AbsFunction(b)) => a.semantic_eq(b),
(SignFunction(a), SignFunction(b)) => calc_sum_eq(&a.params, &b.params),
_ => false,
}
}
fn calc_sum_or_none_eq<'a, T: SemanticEq>(a: &NoneOr<CalcSum<'a, T>>, b: &NoneOr<CalcSum<'a, T>>) -> bool {
match (a, b) {
(NoneOr::Some(a), NoneOr::Some(b)) => calc_sum_eq(a, b),
(NoneOr::None(_), NoneOr::None(_)) => true,
_ => false,
}
}
fn calc_sum_opt_eq<'a, T: SemanticEq>(
a: &Option<Box<'a, CalcSum<'a, T>>>,
b: &Option<Box<'a, CalcSum<'a, T>>>,
) -> bool {
match (a, b) {
(None, None) => true,
(Some(a), Some(b)) => calc_sum_eq(a, b),
_ => false,
}
}
#[node]
#[derive(Peek, Parse, ToCursors, ToSpan, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
#[derive(csskit_derives::NodeWithMetadata)]
#[metadata(declaration_kinds = Computed)]
pub enum CalcOperandSubstitutionFunction<'a, T> {
Math(MathFunction<'a, T>),
Var(VarFunction<'a, CalcOperand<'a, T>>),
Env(EnvFunction<'a, CalcOperand<'a, T>>),
Attr(AttrFunction<'a>),
If(IfFunction<'a, CalcOperand<'a, T>>),
FirstValid(FirstValidFunction<'a, CalcOperand<'a, T>>),
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
pub struct CalcProduct<'a, T> {
pub first: CalcOperand<'a, T>,
pub rest: Vec<'a, (CalcProductOperator, CalcOperand<'a, T>)>,
}
impl<'a, T: SemanticEq> SemanticEq for CalcProduct<'a, T> {
fn semantic_eq(&self, other: &Self) -> bool {
calc_product_eq(self, other)
}
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum CalcProductOperator {
Multiply(T![*]),
Divide(T![/]),
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum CalcValue<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
Number(Number),
Typed(T),
Keyword(CalcKeyword),
TreeCounting(TreeCountingFunction),
Parenthesized(CalcInParens<'a, T>),
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
#[derive(csskit_derives::NodeWithMetadata)]
pub struct CalcInParens<'a, T> {
#[cfg_attr(feature = "visitable", visit(skip))]
pub open: LeftParen,
pub sum: Box<'a, CalcSum<'a, T>>,
#[cfg_attr(feature = "visitable", visit(skip))]
#[semantic_eq(skip)]
pub close: RightParen,
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum CalcKeyword {
#[atom(CssAtomSet::E)]
E(Ident),
#[atom(CssAtomSet::Pi)]
Pi(Ident),
#[atom(CssAtomSet::Infinity)]
Infinity(Ident),
#[atom(CssAtomSet::_NegInfinity)]
NegativeInfinity(Ident),
#[atom(CssAtomSet::NaN)]
NaN(Ident),
}
#[node]
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum RoundingStrategy {
#[atom(CssAtomSet::Nearest)]
Nearest(Ident),
#[atom(CssAtomSet::Up)]
Up(Ident),
#[atom(CssAtomSet::Down)]
Down(Ident),
#[atom(CssAtomSet::ToZero)]
ToZero(Ident),
#[atom(CssAtomSet::LineWidth)]
LineWidth(Ident),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{CssAtomSet, Length};
use css_parse::{assert_parse, assert_parse_error};
type LengthMathFunction<'a> = MathFunction<'a, Length>;
#[test]
fn test_calc_functions() {
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(2 + 3)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "min(1, 2)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "max(3, 4)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "clamp(0, 5, 10)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "round(5.5)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "abs(-5)");
}
#[test]
fn test_clamp() {
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "clamp(none, 5px, 10px)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "clamp(0px, 5px, none)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "clamp(1px, 2px, 3px)");
}
#[test]
fn test_round() {
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "round(5.5, 2)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "round(up, 5.5)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "round(nearest, 5.5, 2)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "round(to-zero, 5.5)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "round(line-width, 5.5)");
}
#[test]
fn test_mod_rem() {
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "mod(18, 5)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "rem(18, 5)");
}
#[test]
fn test_trig_and_exponential() {
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "sin(45deg)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "cos(45deg)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "tan(45deg)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "asin(0.5)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "acos(0.5)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "atan(0.5)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "atan2(1, 1)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "pow(2, 3)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "sqrt(2)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "hypot(1, 2, 3)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "log(8, 2)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "log(8)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "exp(1)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "sign(-5)");
}
#[test]
fn test_calc_keywords() {
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(e)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(pi)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(infinity)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(-infinity)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(NaN)");
}
#[test]
fn test_calc_nesting_and_precedence() {
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(1px + 2px * 3)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc((1px + 2px) * 3)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(1px * 2 / 3)");
}
#[test]
fn test_calc_substitution() {
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(var(--foo) * 2)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(1px + var(--foo))");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(var(--foo, 1px) + var(--bar, 2px))");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "min(var(--foo), 10px)");
}
#[test]
fn test_calc_nested_math_function() {
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(min(1px, 2px) + 3px)");
}
#[test]
fn test_calc_tree_counting_functions() {
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(sibling-index() * 10px)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(10px * sibling-count())");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "min(sibling-index(), 4)");
}
#[test]
fn test_calc_operator_whitespace_sensitivity() {
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(1px + -2px)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(1px*2)");
assert_parse!(CssAtomSet::ATOMS, LengthMathFunction, "calc(1px/2)");
}
#[test]
fn test_calc_errors() {
assert_parse_error!(CssAtomSet::ATOMS, LengthMathFunction, "calc(1px -2px)");
assert_parse_error!(CssAtomSet::ATOMS, LengthMathFunction, "calc()");
assert_parse_error!(CssAtomSet::ATOMS, LengthMathFunction, "clamp(1px, 2px)");
assert_parse_error!(CssAtomSet::ATOMS, LengthMathFunction, "calc(1px + 10%)");
}
}