mirror of
https://github.com/sass/sass.git
synced 2024-09-21 02:27:30 +00:00
parent
1b39070cf4
commit
68944df35e
@ -106,9 +106,7 @@ The [`SpecialFunctionName`] production will be changed to the following:
|
||||
|
||||
### `CalcValue`
|
||||
|
||||
The [`CalcValue`] production will be changed to the following:
|
||||
|
||||
[`CalcValue`]: ../spec/types/calculation.md#calculationexpression
|
||||
The `CalcValue` production will be changed to the following:
|
||||
|
||||
<x><pre>
|
||||
**CalcValue** ::= CalcValue (('+' | '-' | '*' | '/') CalcValue)+
|
||||
|
@ -389,9 +389,7 @@ case-insensitively.
|
||||
### `CssMinMax`
|
||||
|
||||
This proposal replaces the reference to `CalcValue` in the definition of
|
||||
[`CssMinMax`] with `CalcArgument`.
|
||||
|
||||
[`CssMinMax`]: ../spec/types/calculation.md#cssminmax
|
||||
`CssMinMax` with `CalcArgument`.
|
||||
|
||||
> Note that this increases the number of cases where a `MinMaxExpression` will
|
||||
> be parsed as a `CssMinMax` rather than a `FunctionExpression` (for example,
|
||||
|
@ -1,3 +1,7 @@
|
||||
## Draft 3.1
|
||||
|
||||
* Update to accommodate new calculation parsing logic.
|
||||
|
||||
## Draft 3
|
||||
|
||||
* Make a potentially slash-separated number slash-free when passing it as an
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Forward Slash as a Separator: Draft 3
|
||||
# Forward Slash as a Separator: Draft 3.1
|
||||
|
||||
*([Issue](https://github.com/sass/sass/issues/2565), [Changelog](slash-separator.changes.md))*
|
||||
|
||||
@ -15,7 +15,13 @@ operator.
|
||||
* [First-Class `calc()`](#first-class-calc)
|
||||
* [`math()` Syntax](#math-syntax)
|
||||
* [Existing Behavior](#existing-behavior)
|
||||
* [Definitions](#definitions)
|
||||
* [Calculation-Safe Expression](#calculation-safe-expression)
|
||||
* [Syntax](#syntax)
|
||||
* [Procedures](#procedures)
|
||||
* [Evaluating a `FunctionCall` as a Calculation](#evaluating-a-functioncall-as-a-calculation)
|
||||
* [Adjusting Slash Precedence](#adjusting-slash-precedence)
|
||||
* [`SlashListExpression`](#slashlistexpression)
|
||||
* [Semantics](#semantics)
|
||||
* [Slash-Separated Lists](#slash-separated-lists)
|
||||
* [`math.div()` Function](#mathdiv-function)
|
||||
@ -199,6 +205,16 @@ value, it is written as the original numerator followed by `/` followed by the
|
||||
original denominator. If either the original numerator or denominator are
|
||||
themselves slash-separated, they're also written this way.
|
||||
|
||||
## Definitions
|
||||
|
||||
### Calculation-Safe Expression
|
||||
|
||||
Remove "or `/`" from the definition of a [calculation-safe] `ProductExpression`.
|
||||
Add "An unbracketed `SlashListExpression` with more than one element, all of
|
||||
which are calculation-safe" to the list of calculation-safe expressions.
|
||||
|
||||
[calculation-safe]: ../spec/types/calculation.md#calculation-safe-expression
|
||||
|
||||
## Syntax
|
||||
|
||||
> Note that the existing productions being modified have not been defined
|
||||
@ -237,6 +253,86 @@ When a `SlashListExpression` with one or more `/`s is evaluated, it produces a
|
||||
list object whose contents are the values of its constituent
|
||||
`SpaceListExpression`s and whose separator is "slash".
|
||||
|
||||
## Procedures
|
||||
|
||||
### Evaluating a `FunctionCall` as a Calculation
|
||||
|
||||
Replace "evaluating each `Expression`" with "[adjusting slash precedence] in and
|
||||
then evaluating each `Expression`" in [evaluting a `FunctionCall` as a
|
||||
calculation].
|
||||
|
||||
[adjusting slash precedence]: #adjusting-slash-precedence
|
||||
[evaluting a `FunctionCall` as a calculation]: ../spec/types/calculation.md#evaluating-a-functioncall-as-a-calculation
|
||||
|
||||
### Adjusting Slash Precedence
|
||||
|
||||
This algorithm takes a calculation-safe expression `expression` and returns
|
||||
another calculation-safe expression with the precedence of
|
||||
`SlashListExpression`s adjusted to match division precedence.
|
||||
|
||||
* Return a copy of `expression` except, for each `SlashListExpression`:
|
||||
|
||||
* Let `left` be the first element of the list.
|
||||
|
||||
* For each remaining element `right`:
|
||||
|
||||
* If `left` and `right` are both `SumExpression`s:
|
||||
|
||||
* Let `last-left` be the last operand of `left` and `first-right` the
|
||||
first operand of `right`.
|
||||
|
||||
* Set `left` to a `SumExpression` that begins with all operands and
|
||||
operators of `left` except `last-left`, followed by a
|
||||
`SlashListExpression` with elements `last-left` and `first-right`,
|
||||
followed by all operators and operands of `right` except `first-right`.
|
||||
|
||||
> For example, `slash-list(1 + 2, 3 + 4)` becomes `1 + (2 / 3) + 4`.
|
||||
|
||||
* Otherwise, if `left` is a `SumExpression`:
|
||||
|
||||
* Let `last-left` be the last operand of `left`.
|
||||
|
||||
* Set `left` to a `SumExpression` that begins with all operands and
|
||||
operators of `left` except `last-left`, followed by a
|
||||
`SlashListExpression` with elements `last-left` and `right`.
|
||||
|
||||
> For example, `slash-list(1 + 2, 3)` becomes `1 + (2 / 3)`.
|
||||
|
||||
* Otherwise, if `right` is a `SumExpression` or a `ProductExpression`:
|
||||
|
||||
* Let `first-right` be the first operand of `right`.
|
||||
|
||||
* Set `left` to an expression of the same type as `right` that begins a
|
||||
`SlashListExpression` with elements `left` and `first-right`, followed
|
||||
by operators and operands of `right` except `first-right`.
|
||||
|
||||
> For example, `slash-list(1, 2 * 3)` becomes `(1 / 2) * 3`.
|
||||
|
||||
* Otherwise, if `left` is a slash-separated list, add `right` to the end.
|
||||
|
||||
* Otherwise, set `left` to a slash-separated list containing `left` and
|
||||
`right`.
|
||||
|
||||
* Replace each element in `left` with the result of adjusting slash precedence
|
||||
in that element.
|
||||
|
||||
* Replace the `SlashListExpression` with `left` in the returned expression.
|
||||
|
||||
### `SlashListExpression`
|
||||
|
||||
To evaluate a `SlashListExpression` as a calculation value:
|
||||
|
||||
* Let `left` be the result of evaluating the first element of the list as a
|
||||
calculation value.
|
||||
|
||||
* For each remaining element `element`:
|
||||
|
||||
* Let `right` be the result of evaluating `element` as a calculation value.
|
||||
|
||||
* Set `left` to a `CalcOperation` with operator `"/"`, `left`, and `right`.
|
||||
|
||||
* Return `left`.
|
||||
|
||||
## Semantics
|
||||
|
||||
### Slash-Separated Lists
|
||||
|
@ -1 +1 @@
|
||||
2.1.0
|
||||
2.2.0
|
||||
|
@ -958,6 +958,11 @@ message Value {
|
||||
|
||||
// An unquoted string as created by interpolation for
|
||||
// backwards-compatibility with older Sass syntax.
|
||||
//
|
||||
// The compiler must treat this as identical to a `string` option whose
|
||||
// value is `"(" + interpolation + ")"`.
|
||||
//
|
||||
// This field is deprecated and hosts should avoid using it.
|
||||
string interpolation = 3;
|
||||
|
||||
CalculationOperation operation = 4;
|
||||
|
@ -47,18 +47,14 @@ matching is case-insensitive.
|
||||
## Syntax
|
||||
|
||||
<x><pre>
|
||||
**FunctionExpression**¹ ::= [CssMinMax]
|
||||
  | [SpecialFunctionExpression]
|
||||
  | [CalculationExpression]
|
||||
**FunctionExpression**¹ ::= [SpecialFunctionExpression]
|
||||
  | EmptyFallbackVar
|
||||
  | FunctionCall
|
||||
**EmptyFallbackVar**² ::= 'var(' Expression ',' ')'
|
||||
**FunctionCall**⁴ ::= [NamespacedIdentifier] ArgumentInvocation
|
||||
</pre></x>
|
||||
|
||||
[CssMinMax]: types/calculation.md#cssminmax
|
||||
[SpecialFunctionExpression]: syntax.md#specialfunctionexpression
|
||||
[CalculationExpression]: types/calculation.md#calculationexpression
|
||||
[NamespacedIdentifier]: modules.md#syntax
|
||||
|
||||
1: Both `CssMinMax` and `EmptyFallbackVar` take precedence over `FunctionCall`
|
||||
@ -107,6 +103,25 @@ To evaluate a `FunctionCall` `call`:
|
||||
|
||||
* If `function` is null and `name` is not a plain `Identifier`, throw an error.
|
||||
|
||||
* If `function` is null; `name` is case-insensitively equal to `"min"`, `"max"`,
|
||||
`"round"`, or `"abs"`; `call`'s `ArgumentInvocation` doesn't have any
|
||||
`KeywordArgument`s or `RestArgument`s; and all arguments in `call`'s
|
||||
`ArgumentInvocation` are [calculation-safe], return the result of evaluating
|
||||
`call` [as a calculation].
|
||||
|
||||
[calculation-safe]: types/calculation.md#calculation-safe-expression
|
||||
[as a calculation]: types/calculation.md#evaluating-a-functioncall-as-a-calculation
|
||||
|
||||
> For calculation functions that overlap with global Sass function names, we
|
||||
> want anything Sass-specific like this to end up calling the Sass function.
|
||||
> For all other calculation functions, we want those constructs to throw an
|
||||
> error (which they do when evaluating `call` [as a calculation]).
|
||||
|
||||
* If `function` is null and `name` is case-insensitively equal to `"calc"`,
|
||||
`"clamp"`, `"hypot"`, `"sin"`, `"cos"`, `"tan"`, `"asin"`, `"acos"`, `"atan"`,
|
||||
`"sqrt"`, `"exp"`, `"sign"`, `"mod"`, `"rem"`, `"atan2"`, `"pow"`, or `"log"`,
|
||||
return the result of evaluating `call` [as a calculation].
|
||||
|
||||
* If `function` is null, set it to the [global function](#global-functions)
|
||||
named `name`.
|
||||
|
||||
|
@ -237,9 +237,15 @@ hashCode(): number;
|
||||
|
||||
### `CalculationInterpolation`
|
||||
|
||||
The JS API representation of a Sass [`CalculationInterpolation`].
|
||||
A deprecated alternative JS API representation of an unquoted Sass string that's
|
||||
always surrounded by parentheses. It's never returned by the Sass compiler, but
|
||||
for backwards-compatibility users may still construct it and pass it to the Sass
|
||||
compiler.
|
||||
|
||||
[`CalculationInterpolation`]: ../../types/calculation.md#types
|
||||
> `CalculationInterpolation`s are no longer generated by the Sass compiler,
|
||||
> because it can now tell at evaluation time whether an interpolation was
|
||||
> originally surrounded by parentheses. However, until we make a breaking
|
||||
> revision of the JS API, users may continue to pass `CalculationInterpolation`s
|
||||
|
||||
```ts
|
||||
export class CalculationInterpolation implements ValueObject {
|
||||
@ -247,13 +253,12 @@ export class CalculationInterpolation implements ValueObject {
|
||||
|
||||
#### `internal`
|
||||
|
||||
A private property like [`Value.internal`] that refers to a Sass
|
||||
[`CalculationInterpolation`].
|
||||
A private property like [`Value.internal`] that refers to a Sass string.
|
||||
|
||||
#### Constructor
|
||||
|
||||
Creates a Sass [`CalculationInterpolation`] by setting the `value` field to the
|
||||
`value` argument and returns it.
|
||||
Creates a `CalculationInterpolation` with `internal` set to an unquoted Sass
|
||||
string with text `"(" + value + ")"` and returns it.
|
||||
|
||||
```ts
|
||||
constructor(value: string);
|
||||
@ -261,9 +266,10 @@ constructor(value: string);
|
||||
|
||||
#### `value`
|
||||
|
||||
Returns [`internal`][ci-internal]'s `value` field.
|
||||
Returns [`internal`][ci-internal]'s `value` field's text, without the leading
|
||||
and trailing parentheses.
|
||||
|
||||
[ci-internal]: #internal-2
|
||||
[ci-internal]: #internal-1
|
||||
|
||||
```ts
|
||||
get value(): string;
|
||||
@ -271,7 +277,8 @@ get value(): string;
|
||||
|
||||
#### `equals`
|
||||
|
||||
Whether [`internal`][ci-internal] is equal to `other.internal` in Sass.
|
||||
Whether `other` is a `CalculationInterpolation` and [`internal`][ci-internal] is
|
||||
equal to `other.internal` in Sass.
|
||||
|
||||
```ts
|
||||
equals(other: unknown): boolean;
|
||||
|
@ -19,7 +19,7 @@ export {
|
||||
CalculationValue,
|
||||
CalculationOperator,
|
||||
CalculationOperation,
|
||||
CalculationInterpolation
|
||||
CalculationInterpolation,
|
||||
} from './calculation';
|
||||
export {SassColor} from './color';
|
||||
export {SassFunction} from './function';
|
||||
|
@ -2,88 +2,48 @@
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Syntax](#syntax)
|
||||
* [`CalculationExpression`](#calculationexpression)
|
||||
* [`CssMinMax`](#cssminmax)
|
||||
* [Definitions](#definitions)
|
||||
* [Calculation-Safe Expression](#calculation-safe-expression)
|
||||
* [Types](#types)
|
||||
* [Operations](#operations)
|
||||
* [Equality](#equality)
|
||||
* [Serialization](#serialization)
|
||||
* [Calculation](#calculation)
|
||||
* [`CalculationOperation`](#calculationoperation)
|
||||
* [`CalculationInterpolation`](#calculationinterpolation)
|
||||
* [`Number`](#number)
|
||||
* [Procedures](#procedures)
|
||||
* [Evaluating a `FunctionCall` as a Calculation](#evaluating-a-functioncall-as-a-calculation)
|
||||
* [Evaluating an Expression as a Calculation Value](#evaluating-an-expression-as-a-calculation-value)
|
||||
* [Simplifying a Calculation](#simplifying-a-calculation)
|
||||
* [Simplifying a `CalculationValue`](#simplifying-a-calculationvalue)
|
||||
* [Semantics](#semantics)
|
||||
* [`CalcExpression`](#calcexpression)
|
||||
* [`ClampExpression`](#clampexpression)
|
||||
* [`CssMinMax`](#cssminmax-1)
|
||||
* [`CalcArgument`](#calcargument)
|
||||
* [`CalcSum`](#calcsum)
|
||||
* [`CalcProduct`](#calcproduct)
|
||||
* [`CalcValue`](#calcvalue)
|
||||
* [`ParenthesizedVar`](#parenthesizedvar)
|
||||
* [`FunctionExpression` and `Variable`](#functionexpression-and-variable)
|
||||
* [`SumExpression` and `ProductExpression`](#sumexpression-and-productexpression)
|
||||
* [`SpaceListExpression`](#spacelistexpression)
|
||||
* [`ParenthesizedExpression`](#parenthesizedexpression)
|
||||
* [`InterpolatedIdentifier`](#interpolatedidentifier)
|
||||
|
||||
## Syntax
|
||||
## Definitions
|
||||
|
||||
### `CalculationExpression`
|
||||
### Calculation-Safe Expression
|
||||
|
||||
This production is parsed in a SassScript context when an expression is expected
|
||||
and the input stream starts with an identifier with value `calc` or `clamp`
|
||||
(ignoring case) followed immediately by `(`.
|
||||
An expression is "calculation-safe" if it is one of:
|
||||
|
||||
The grammar for this production is:
|
||||
* A [`FunctionExpression`].
|
||||
* A `ParenthesizedExpression` whose contents is calculation-safe.
|
||||
* A `SumExpression` whose operands are calculation-safe.
|
||||
* A `ProductExpression` whose operator is `*` or `/` and whose operands are
|
||||
calculation-safe.
|
||||
* A `Number`.
|
||||
* A `Variable`.
|
||||
* An `InterpolatedIdentifier`.
|
||||
* An unbracketed `SpaceListExpression` with more than one element, whose
|
||||
elements are all calculation-safe.
|
||||
|
||||
<x><pre>
|
||||
**CalculationExpression** ::= CalcExpression | ClampExpression
|
||||
**CalcExpression** ::= 'calc('¹ CalcArgument ')'
|
||||
**ClampExpression** ::= 'clamp('¹ CalcArgument ( ',' CalcArgument ){2} ')'
|
||||
**CalcArgument**² ::= InterpolatedDeclarationValue† | CalcSum
|
||||
**CalcSum** ::= CalcProduct (('+' | '-')³ CalcProduct)\*
|
||||
**CalcProduct** ::= CalcValue (('\*' | '/') CalcValue)\*
|
||||
**CalcValue** ::= ParenthesizedVar
|
||||
  | '(' CalcArgument⁴ ')'
|
||||
  | CalculationExpression
|
||||
  | CssMinMax
|
||||
  | FunctionExpression⁵
|
||||
  | Number
|
||||
  | Variable†
|
||||
  | [\<ident-token>]
|
||||
**ParenthesizedVar** ::= '(' 'var('¹ ArgumentInvocation ')' ')'
|
||||
</pre></x>
|
||||
[`FunctionExpression`]: ../functions.md#syntax
|
||||
|
||||
[\<ident-token>]: https://drafts.csswg.org/css-syntax-3/#ident-token-diagram
|
||||
|
||||
1: The strings `calc(`, `clamp(`, and `var(` are matched case-insensitively.
|
||||
|
||||
2: A `CalcArgument` is only parsed as an `InterpolatedDeclarationValue` if it
|
||||
includes interpolation, unless that interpolation is within a region bounded by
|
||||
parentheses (a `FunctionExpression` counts as parentheses).
|
||||
|
||||
3: Whitespace is required around these `"+"` and `"-"` tokens.
|
||||
|
||||
4: This `CalcArgument` cannot begin with `var(`, case-insensitively.
|
||||
|
||||
5: This `FunctionExpression` cannot begin with `min(`, `max(`, or `clamp(`,
|
||||
case-insensitively.
|
||||
|
||||
†: These productions are invalid in plain CSS syntax.
|
||||
|
||||
> The `CalcArgument` production provides backwards-compatibility with the
|
||||
> historical use of interpolation to inject SassScript values into `calc()`
|
||||
> expressions. Because interpolation could inject any part of a `calc()`
|
||||
> expression regardless of syntax, for full compatibility it's necessary to
|
||||
> parse it very expansively.
|
||||
|
||||
### `CssMinMax`
|
||||
|
||||
<x><pre>
|
||||
**CssMinMax** ::= ('min(' | 'max(')¹ CalcArgument (',' CalcArgument)* ')'
|
||||
</pre></x>
|
||||
|
||||
1: The strings `min(` and `max(` are matched case-insensitively.
|
||||
> Because calculations have special syntax in CSS, only a subset of SassScript
|
||||
> expressions are valid (and these are interpreted differently than elsewhere).
|
||||
|
||||
## Types
|
||||
|
||||
@ -98,14 +58,9 @@ interface Calculation {
|
||||
type CalculationValue =
|
||||
| Number
|
||||
| UnquotedString
|
||||
| CalculationInterpolation
|
||||
| CalculationOperation
|
||||
| Calculation;
|
||||
|
||||
interface CalculationInterpolation {
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface CalculationOperation {
|
||||
operator: '+' | '-' | '*' | '/';
|
||||
left: CalculationValue;
|
||||
@ -136,8 +91,8 @@ Two calculations are considered equal if their names are equal, they have the
|
||||
same number of arguments, and each argument in one calculation is equal to the
|
||||
corresponding argument in the other.
|
||||
|
||||
`CalculationOperation` and `CalculationInterpolation` values are equal if each
|
||||
field in one value is equal to the corresponding field in the other.
|
||||
`CalculationOperation` values are equal if each field in one value is equal to
|
||||
the corresponding field in the other.
|
||||
|
||||
### Serialization
|
||||
|
||||
@ -153,19 +108,14 @@ To serialize a `CalculationOperation`:
|
||||
* Let `left` and `right` be the result of serializing the left and right values,
|
||||
respectively.
|
||||
|
||||
* If either:
|
||||
|
||||
* the left value is a `CalculationInterpolation`, or
|
||||
* the operator is `"*"` or `"/"` and the left value is a
|
||||
`CalculationOperation` with operator `"+"` or `"-"`,
|
||||
|
||||
emit `"("` followed by `left` followed by `")"`. Otherwise, emit `left`.
|
||||
* If the operator is `"*"` or `"/"` and the left value is a
|
||||
`CalculationOperation` with operator `"+"` or `"-"`, emit `"("` followed by
|
||||
`left` followed by `")"`. Otherwise, emit `left`.
|
||||
|
||||
* Emit `" "`, then the operator, then `" "`.
|
||||
|
||||
* If either:
|
||||
|
||||
* the right value is a `CalculationInterpolation`, or
|
||||
* the operator is `"*"` or `"-"` and the right value is a
|
||||
`CalculationOperation` with operator `"+"` or `"-"`, or
|
||||
* the operator is `"/"` and the right value is a `CalculationOperation`,
|
||||
@ -174,10 +124,6 @@ To serialize a `CalculationOperation`:
|
||||
|
||||
emit `"("` followed by `right` followed by `")"`. Otherwise, emit `right`.
|
||||
|
||||
#### `CalculationInterpolation`
|
||||
|
||||
To serialize a `CalculationInterpolation`, emit its `value`.
|
||||
|
||||
#### `Number`
|
||||
|
||||
To serialize a `Number` within a `CalculationExpression`:
|
||||
@ -197,6 +143,34 @@ To serialize a `Number` within a `CalculationExpression`:
|
||||
|
||||
## Procedures
|
||||
|
||||
### Evaluating a `FunctionCall` as a Calculation
|
||||
|
||||
This algorithm takes a [`FunctionCall`] `call` whose name is a plain identifier
|
||||
and returns a number or a calculation.
|
||||
|
||||
* If `call`'s `ArgumentInvocation` contains one or more `KeywordArgument`s or
|
||||
one or more `RestArgument`s, throw an error.
|
||||
|
||||
* Let `calc` be a calculation whose name is the lower-case value of `call`'s
|
||||
name and whose arguments are the result of evaluating each `Expression` in
|
||||
`call`'s `ArgumentInvocation` [as a calculation value].
|
||||
|
||||
[as a calculation value]: #evaluating-an-expression-as-a-calculation-value
|
||||
|
||||
* Return the result of [simplifying](#simplifying-a-calculation) `calc`.
|
||||
|
||||
### Evaluating an Expression as a Calculation Value
|
||||
|
||||
This algorithm takes an expression `expression` and returns a
|
||||
`CalculationValue`.
|
||||
|
||||
* If `expression` isn't [calculation-safe], throw an error.
|
||||
|
||||
* Otherwise, evaluate `expression` using the semantics defined in the
|
||||
[Semantics] section if available, or the standard semantics otherwise.
|
||||
|
||||
[Semantics]: #semantics
|
||||
|
||||
### Simplifying a Calculation
|
||||
|
||||
This algorithm takes a calculation `calc` and returns a number or a calculation.
|
||||
@ -207,31 +181,205 @@ This algorithm takes a calculation `calc` and returns a number or a calculation.
|
||||
* If `calc` was parsed from an expression within a `SupportsDeclaration`'s
|
||||
`Expression`, but outside any interpolation, return a `calc` as-is.
|
||||
|
||||
* Let `arguments` be the result of [simplifying](#simplifying-a-calculationvalue) each
|
||||
of `calc`'s arguments.
|
||||
* Let `arguments` be the result of [simplifying] each of `calc`'s arguments.
|
||||
|
||||
* If `calc`'s name is `"calc"`, the syntax guarantees that `arguments` contain
|
||||
only a single argument. If that argument is a number or calculation, return
|
||||
it.
|
||||
[simplifying]: #simplifying-a-calculationvalue
|
||||
|
||||
* If `calc`'s name is `"clamp"`, `arguments` has fewer than three elements, and
|
||||
none of those are unquoted strings or `CalculationInterpolation`s, throw an
|
||||
* If `calc`'s name is `"calc"` and `arguments` contains exactly a single number
|
||||
or calculation, return it.
|
||||
|
||||
* If `calc`'s name is `"mod"`, `"rem"`, `"atan2"`, or `"pow"`; `arguments` has
|
||||
fewer than two elements; and none of those are unquoted strings, throw an
|
||||
error.
|
||||
|
||||
> It's valid to write `clamp(var(--three-args))` or `clamp(#{"1, 2, 3"})`, but
|
||||
> otherwise `clamp()` has to have three physical arguments.
|
||||
> It's valid to write `pow(var(--two-args))` or `pow(#{"2, 3"})`, but
|
||||
> otherwise calculations' arguments must match the expected number.
|
||||
|
||||
* If `calc`'s name is `"clamp"` and `arguments` are all
|
||||
numbers:
|
||||
* If `calc`'s name is `"sin"`, `"cos"`, `"tan"`, `"asin"`, `"acos"`, `"atan"`,
|
||||
`"sqrt"`, `"log"`, or `"round"` and `arguments` contains exactly a single
|
||||
number, return the result of passing that number to the function in
|
||||
[`sass:math`] whose name matches `calc`'s.
|
||||
|
||||
* If those arguments' are mutually [compatible], return the result of calling
|
||||
`math.clamp()` with those arguments.
|
||||
[`sass:math`]: ../built-in-modules/math.md
|
||||
|
||||
* Otherwise, if any two of those arguments are [definitely-incompatible],
|
||||
throw an error.
|
||||
> The `sass:math` functions will check units here for the functions that
|
||||
> require specific or no units.
|
||||
|
||||
* If `calc`'s name is `"abs"` and `arguments` contains exactly a single number
|
||||
with [known units], return the result of passing that number to the function
|
||||
in [`sass:math`] whose name matches `calc`'s.
|
||||
|
||||
[known units]: number.md#known-units
|
||||
|
||||
* If `calc`'s name is `"exp"` and `arguments` contains exactly a single number
|
||||
`number`, return the result of calling `math.pow(math.$e, number)`.
|
||||
|
||||
> This will throw an error if the argument has units.
|
||||
|
||||
* If `calc`'s name is `"sign"` and `arguments` contains exactly a single number
|
||||
`number` with [known units]:
|
||||
|
||||
* If `number`'s value is positive, return `1`.
|
||||
* If `number`'s value is negative, return `-1`.
|
||||
* Otherwise, return a unitless number with the same value as `number`.
|
||||
|
||||
> In this case, `number` is either `+0`, `-0`, or NaN.
|
||||
|
||||
> To match CSS's behavior, these computations *don't* use fuzzy comparisons.
|
||||
|
||||
* If `calc`'s name is `"log"`:
|
||||
|
||||
* If any argument is a number with units, throw an error.
|
||||
|
||||
* Otherwise, if `arguments` contains exactly two numbers, return the result of
|
||||
passing its arguments to the [`log()` function] in [`sass:math`].
|
||||
|
||||
[`log()` function]: ../built-in-modules/math.md#log
|
||||
|
||||
* If `calc`'s name is `"pow"`:
|
||||
|
||||
* If any argument is a number with units, throw an error.
|
||||
|
||||
* Otherwise, if `arguments` contains exactly two numbers, return the result of
|
||||
passing those numbers to the [`pow()` function] in [`sass:math`].
|
||||
|
||||
[`pow()` function]: ../built-in-modules/math.md#pow
|
||||
|
||||
* If `calc`'s name is `"atan2"` and `arguments` contains two numbers which both
|
||||
have [known units], return the result of passing those numbers to the
|
||||
[`atan2()` function] in [`sass:math`].
|
||||
|
||||
> This will throw an error if either argument has units.
|
||||
>
|
||||
> `atan2()` passes percentages along to the browser because they may resolve
|
||||
> to negative values, and `atan2(-x, -y) != atan2(x, y)`.
|
||||
|
||||
[`atan2()` function]: ../built-in-modules/math.md#atan2
|
||||
|
||||
* If `calc`'s name is `"mod"` or `"rem"`:
|
||||
|
||||
* If `arguments` has only one element and it's not an unquoted string, throw
|
||||
an error.
|
||||
|
||||
* Otherwise, if `arguments` contains exactly two numbers `dividend` and
|
||||
`modulus`:
|
||||
|
||||
* If `dividend` and `modulus` are [definitely-incompatible], throw an error.
|
||||
|
||||
* If `dividend` and `modulus` are mutually [compatible]:
|
||||
|
||||
* Let `result` be the result of `dividend % modulus`.
|
||||
|
||||
* If `calc`'s name is `"rem"`, and if `dividend` is positive and `modulus`
|
||||
is negative or vice versa:
|
||||
|
||||
* If `modulus` is infinite, return `dividend`.
|
||||
* If `result` [exactly equals] 0, return `-result`.
|
||||
* Otherwise, return `result - modulus`.
|
||||
|
||||
* Otherwise, return `result`.
|
||||
|
||||
[compatible]: number.md#compatible-units
|
||||
[definitely-incompatible]: number.md#possibly-compatible-numbers
|
||||
[exactly equals]: number.md#exact-equality
|
||||
|
||||
* If `calc`'s name is `"round"`:
|
||||
|
||||
* If `arguments` has exactly three elements, set `strategy`, `number`, and
|
||||
`step` to those arguments respectively.
|
||||
|
||||
* Otherwise, if `arguments` has exactly two elements:
|
||||
|
||||
* If the first element is an unquoted string or interpolation with value
|
||||
`"nearest"`, `"up"`, `"down"`, or `"to-zero"`, and the second argument
|
||||
isn't an unquoted string, throw an error.
|
||||
|
||||
> Normally we allow unquoted strings anywhere in a calculation, but this
|
||||
> helps catch the likely error of a user accidentally writing `round(up,
|
||||
> 10px)` without realizing that it needs a third argument.
|
||||
|
||||
* Otherwise, set `number` and `step` to the two arguments respectively and
|
||||
`strategy` to an unquoted string with value `"nearest"`.
|
||||
|
||||
* Otherwise, if the single argument isn't an unquoted string, throw an error.
|
||||
|
||||
* If `strategy`, `number`, and `step` are set:
|
||||
|
||||
* If `strategy` isn't a [special variable string], nor is it an unquoted
|
||||
string or interpolation with value `"nearest"`, `"up"`, `"down"`, or
|
||||
`"to-zero"`, throw an error.
|
||||
|
||||
* If `strategy` is an unquoted string or interpolation and both `number` and
|
||||
`step` are numbers:
|
||||
|
||||
* If `number` and `step` are [definitely-incompatible], throw an error.
|
||||
|
||||
* If `number` and `step` are mutually [compatible]:
|
||||
|
||||
* If `number`'s and `step`'s values are both infinite, if `step` is
|
||||
[exactly equal] to 0, or if either `number`'s or `step`'s values are
|
||||
NaN, return NaN with the same units as `number`.
|
||||
|
||||
* If `number`'s value is infinite, return `number`.
|
||||
|
||||
* If `step`'s value is infinite:
|
||||
|
||||
* If `strategy`'s value is `"nearest"` or `"to-zero"`, return `+0` if
|
||||
`number`'s value is positive or `+0`, and `-0` otherwise.
|
||||
|
||||
* If `strategy`'s value is `"up"`, return positive infinity if
|
||||
`number`'s value is positive, `+0` if `number`'s value is `+0`, and
|
||||
`-0` otherwise.
|
||||
|
||||
* If `strategy`'s value is `"down"`, return negative infinity if
|
||||
`number`'s value is negative, `-0` if `number`'s value is `-0`, and
|
||||
`+0` otherwise.
|
||||
|
||||
* Set `number` and `step` to the result of [matching units] for `number`
|
||||
and `step`.
|
||||
|
||||
* If `number`'s value is [exactly equal] to `step`'s, return `number`.
|
||||
|
||||
* Let `upper` and `lower` be the two integer multiples of `step` which
|
||||
are closest to `number` such that `upper` is greater than `lower`. If
|
||||
`upper` would be 0, it's specifically `-0`; if `lower` would be zero,
|
||||
it's specifically `-0`.
|
||||
|
||||
* If `strategy`'s value is `"nearest"`, return whichever of `upper` and
|
||||
`lower` has the smallest absolute distance from `number`. If both have
|
||||
an equal difference, return `upper`.
|
||||
|
||||
* If `strategy`'s value is `"up"`, return `upper`.
|
||||
|
||||
* If `strategy`'s value is `"down"`, return `lower`.
|
||||
|
||||
* If `strategy`'s value is `"to-zero"`, return whichever of `upper` and
|
||||
`lower` has the smallest absolute difference from 0.
|
||||
|
||||
[special variable string]: ../functions.md#special-variable-string
|
||||
|
||||
* If `calc`'s name is `"clamp"`:
|
||||
|
||||
* If `arguments` has fewer than three elements, and none of those are unquoted
|
||||
strings, throw an error.
|
||||
|
||||
* Otherwise, if any two elements of `arguments` are [definitely-incompatible]
|
||||
numbers, throw an error.
|
||||
|
||||
* Otherwise, if `arguments` are all mutually [compatible] numbers, return the
|
||||
result of calling `math.clamp()` with those arguments.
|
||||
|
||||
* If `calc`'s name is `"hypot"`:
|
||||
|
||||
* If any two elements of `arguments` are [definitely-incompatible] numbers,
|
||||
throw an error.
|
||||
|
||||
* Otherwise, if all `arguments` are all numbers with [known units] that are
|
||||
mutually [compatible], return the result of calling `math.hypot()` with
|
||||
those arguments.
|
||||
|
||||
> `hypot()` has an exemption for percentages because it squares its inputs,
|
||||
> so `hypot(-x, -y) != -hypot(x, y)`.
|
||||
|
||||
* If `calc`'s name is `"min"` or `"max"` and `arguments` are all numbers:
|
||||
|
||||
@ -260,19 +408,25 @@ This algorithm takes a `CalculationValue` `value` and returns a
|
||||
> This algorithm is intended to return a value that's CSS-semantically identical
|
||||
> to the input.
|
||||
|
||||
* If `value` is a number, unquoted string, or `CalculationInterpolation`, return
|
||||
it as-is.
|
||||
* If `value` is a number or unquoted string, return it as-is.
|
||||
|
||||
* If `value` is a calculation:
|
||||
|
||||
* Let `result` be the result of [simplifying] `value`.
|
||||
|
||||
* If `result` is a calculation whose name is `"calc"`, return `result`'s
|
||||
single argument.
|
||||
* If `result` isn't a calculation whose name is `"calc"`, return `result`.
|
||||
|
||||
* Otherwise, return `result`.
|
||||
* If `result`'s argument isn't an unquoted string, return `result`'s argument.
|
||||
|
||||
[simplifying]: #simplifying-a-calculation
|
||||
* If `result`'s argument begins case-insensitively with `"var("`; or if it
|
||||
contains whitespace, `"/"`, or `"*"`; return `"(" +` result's argument `+
|
||||
")"` as an unquoted string.
|
||||
|
||||
> This is ensures that values that could resolve to operations end up
|
||||
> parenthesized if used in other operations. It's potentially a little
|
||||
> overzealous, but that's unlikely to be a major problem given that the
|
||||
> output is still smaller than including the full `calc()` and we don't want
|
||||
> to encourage users to inject calculations with interpolation anyway.
|
||||
|
||||
* Otherwise, `value` must be a `CalculationOperation`. Let `left` and `right` be
|
||||
the result of simplifying `value.left` and `value.right`, respectively.
|
||||
@ -313,133 +467,111 @@ This algorithm takes a `CalculationValue` `value` and returns a
|
||||
|
||||
## Semantics
|
||||
|
||||
### `CalcExpression`
|
||||
The following semantics only apply when evaluating expressions [as calculation
|
||||
values].
|
||||
|
||||
To evaluate a `CalcExpression`:
|
||||
[as calculation values]: #evaluating-an-expression-as-a-calculation-value
|
||||
|
||||
* Let `calc` be a calculation whose name is `"calc"` and whose only argument is
|
||||
the result of [evaluating the expression's `CalcArgument`](#calcargument).
|
||||
### `FunctionExpression` and `Variable`
|
||||
|
||||
* Return the result of [simplifying] `calc`.
|
||||
To evaluate a `FunctionExpression` or a `Variable` as a calculation value,
|
||||
evaluate it using the standard semantics. If the result is a number, an unquoted
|
||||
string, or a calculation, return it. Otherwise, throw an error.
|
||||
|
||||
### `ClampExpression`
|
||||
> Allowing variables to return unquoted strings here supports referential
|
||||
> transparency, so that `$var: fn(); calc($var)` works the same as `calc(fn())`.
|
||||
|
||||
To evaluate a `ClampExpression`:
|
||||
### `SumExpression` and `ProductExpression`
|
||||
|
||||
* Let `clamp` be a calculation whose name is `"clamp"` and whose arguments are the
|
||||
results of [evaluating the expression's `CalcArgument`s](#calcargument).
|
||||
To evaluate a `SumExpresssion` or a `ProductExpression` as a calculation value:
|
||||
|
||||
* Return the result of [simplifying] `clamp`.
|
||||
* Let `left` be the result of evaluating the first operand as a calculation
|
||||
value.
|
||||
|
||||
### `CssMinMax`
|
||||
* For each remaining `"+"`, `"-"`, `"*"`, or `"/"` token `operator` and operand
|
||||
`operand`:
|
||||
|
||||
To evaluate a `CssMinMax`:
|
||||
|
||||
* Let `calc` be a calculation whose name is `"min"` or `"max"` according to the
|
||||
`CssMinMax`'s first token, and whose arguments are the results of [evaluating
|
||||
the expression's `CalcArgument`s](#calcargument).
|
||||
|
||||
* Return the result of [simplifying] `calc`.
|
||||
|
||||
### `CalcArgument`
|
||||
|
||||
To evaluate a `CalcArgument` production `argument` into a `CalculationValue` object:
|
||||
|
||||
* If `argument` is an `InterpolatedDeclarationValue`, evaluate it and return a
|
||||
`CalculationInterpolation` whose `value` is the resulting string.
|
||||
|
||||
* Otherwise, return the result of [evaluating `argument`'s
|
||||
`CalcValue`](#calcvalue).
|
||||
|
||||
### `CalcSum`
|
||||
|
||||
To evaluate a `CalcSum` production `sum` into a `CalculationValue` object:
|
||||
|
||||
* Left `left` be the result of evaluating the first `CalcProduct`.
|
||||
|
||||
* For each remaining "+" or "-" token `operator` and `CalcProduct` `product`:
|
||||
|
||||
* Let `right` be the result of evaluating `product`.
|
||||
* Let `right` be the result of evaluating `operand` as a calculation value.
|
||||
|
||||
* Set `left` to a `CalcOperation` with `operator`, `left`, and `right`.
|
||||
|
||||
* Return `left`.
|
||||
|
||||
### `CalcProduct`
|
||||
### `SpaceListExpression`
|
||||
|
||||
To evaluate a `CalcProduct` production `product` into a `CalculationValue`
|
||||
object:
|
||||
To evaluate a `SpaceListExpresssion` as a calculation value:
|
||||
|
||||
* Left `left` be the result of evaluating the first `CalcValue`.
|
||||
* Let `elements` be the results of evaluating each element as a calculation
|
||||
value.
|
||||
|
||||
* For each remaining "*" or "/" token `operator` and `CalcValue` `value`:
|
||||
* If `elements` has two adjacent elements that aren't unquoted strings, throw an
|
||||
error.
|
||||
|
||||
* Let `right` be the result of evaluating `value`.
|
||||
> This ensures that valid CSS constructs like `calc(1 var(--plus-two))` and
|
||||
> similar Sass constructs like `calc(1 #{"+ 2"})` work while preventing clear
|
||||
> errors like `calc(1 2)`.
|
||||
>
|
||||
> This does allow errors like `calc(a b)`, but the complexity of verifying
|
||||
> that the unquoted strings could actually be a partial operation isn't worth
|
||||
> the benefit of eagerly producing an error in this edge case.
|
||||
|
||||
* Set `left` to a `CalcOperation` with `operator`, `left`, and `right` as its
|
||||
values.
|
||||
* Let `serialized` be an empty list.
|
||||
|
||||
* Return `left`.
|
||||
* For each `element` of `elements`:
|
||||
|
||||
### `CalcValue`
|
||||
* Let `css` be the result of [serializing] `element`.
|
||||
|
||||
To evaluate a `CalcValue` production `value` into a `CalculationValue` object:
|
||||
[serializing]: #serialization
|
||||
|
||||
* If `value` is a `CalcArgument`, `CssMinMax`, `Number`, or `ParenthesizedVar`,
|
||||
return the result of evaluating it.
|
||||
* If `element` is a `CalcOperation` that was produced by evaluating a
|
||||
`ParenthesizedExpression`, set `css` to `"(" + css + ")"`.
|
||||
|
||||
* If `value` is a `FunctionExpression` or `Variable`, evaluate it. If the result
|
||||
is a number, an unquoted string, or a calculation, return it. Otherwise, throw
|
||||
an error.
|
||||
* Append `css` to `serialized`.
|
||||
|
||||
> Allowing variables to return unquoted strings here supports referential
|
||||
> transparency, so that `$var: fn(); calc($var)` works the same as
|
||||
> `calc(fn())`.
|
||||
* Return an unquoted strings whose contents are the elements of `serialized`
|
||||
separated by `" "`.
|
||||
|
||||
* If `value` is case-insensitively equal to `pi`, return 3.141592653589793.
|
||||
### `ParenthesizedExpression`
|
||||
|
||||
> This is the closest double approximation of the mathematical constant π.
|
||||
|
||||
* If `value` is case-insensitively equal to `e`, return 2.718281828459045.
|
||||
|
||||
> This is the closest double approximation of the mathematical constant e.
|
||||
|
||||
* If `value` is case-insensitively equal to `infinity`, return the double
|
||||
`Infinity`.
|
||||
|
||||
* If `value` is case-insensitively equal to `-infinity`, return the double
|
||||
`-Infinity`.
|
||||
|
||||
* If `value` is case-insensitively equal to `nan`, return the double `NaN`.
|
||||
|
||||
* If `value` is any other `<identifier>`, return an `UnquotedString` with
|
||||
`value` as its contents.
|
||||
|
||||
### `ParenthesizedVar`
|
||||
|
||||
> If a `var()` is written directly within parentheses, it's necessary to
|
||||
> preserve those parentheses. CSS resolves `var()` by literally replacing the
|
||||
> function with the value of the variable and *then* parsing the surrounding
|
||||
> context.
|
||||
> If a `var()` or an interpolation is written directly within parentheses, it's
|
||||
> necessary to preserve those parentheses. CSS resolves `var()` by literally
|
||||
> replacing the function with the value of the variable and *then* parsing the
|
||||
> surrounding context.
|
||||
>
|
||||
> For example, if `--ratio: 2/3`, `calc(1 / (var(--ratio)))` is parsed as
|
||||
> `calc(1 / (2/3)) = calc(3/2)` but `calc(1 / var(--ratio))` is parsed as
|
||||
> `calc(1 / 2/3) = calc(1/6)`.
|
||||
|
||||
To evaluate a `ParenthesizedVar` production `value` into an unquoted string:
|
||||
To evaluate a `ParenthesizedExpression` with contents `expression` as a
|
||||
calculation value:
|
||||
|
||||
* Let `function` be a [`FunctionCall`] with `"var"` as its
|
||||
[`NamespacedIdentifier`] and with `value`'s `ArgumentInvocation`.
|
||||
* Let `result` be the result of evaluating `expression` as a calculation value.
|
||||
|
||||
[`FunctionCall`]: ../functions.md#syntax
|
||||
[`NamespacedIdentifier`]: ../modules.md#syntax
|
||||
* If `result` is an unquoted string, return `"(" + result + ")"` as an unquoted
|
||||
string.
|
||||
|
||||
* Let `result` be the result of evaluating `function`.
|
||||
* Otherwise, return `result`.
|
||||
|
||||
* If `result` is a number or a calculation, return it.
|
||||
### `InterpolatedIdentifier`
|
||||
|
||||
> This could happen if the user defines a `var` function in Sass.
|
||||
To evaluate an `InterpolatedIdentifier` `ident` as a calculation value:
|
||||
|
||||
* If `result` is not an unquoted string, throw an error.
|
||||
* If `ident` is case-insensitively equal to `pi`, return 3.141592653589793.
|
||||
|
||||
* Return `"(" + result + ")"` as an unquoted string.
|
||||
> This is the closest double approximation of the mathematical constant π.
|
||||
|
||||
* If `ident` is case-insensitively equal to `e`, return 2.718281828459045.
|
||||
|
||||
> This is the closest double approximation of the mathematical constant e.
|
||||
|
||||
* If `ident` is case-insensitively equal to `infinity`, return the double
|
||||
`Infinity`.
|
||||
|
||||
* If `ident` is case-insensitively equal to `-infinity`, return the double
|
||||
`-Infinity`.
|
||||
|
||||
* If `ident` is case-insensitively equal to `nan`, return the double `NaN`.
|
||||
|
||||
* Otherwise, return the result of evaluating `ident` using standard semantics.
|
||||
|
||||
> This will be an `UnquotedString`.
|
||||
|
@ -10,6 +10,8 @@
|
||||
* [Compatible Units](#compatible-units)
|
||||
* [Possibly-Compatible Units](#possibly-compatible-units)
|
||||
* [Possibly-Compatible Numbers](#possibly-compatible-numbers)
|
||||
* [Known Units](#known-units)
|
||||
* [Exact Equality](#exact-equality)
|
||||
* [Fuzzy Equality](#fuzzy-equality)
|
||||
* [Integer](#integer)
|
||||
* [Potentially Slash-Separated Number](#potentially-slash-separated-number)
|
||||
@ -159,12 +161,33 @@ Two numbers are *definitely-incompatible* if they are not possibly-compatible.
|
||||
> complex units, but in practice these numbers are already flagged as errors
|
||||
> prior to any possible-compatibility checks.
|
||||
|
||||
### Known Units
|
||||
|
||||
A number has *known units* unless it has unit `%`.
|
||||
|
||||
> This is relevant for calculations, because in plain CSS they resolve
|
||||
> percentages before doing their operations. This means that any non-linear
|
||||
> operations involving percentages must be passed through to plain CSS rather
|
||||
> than handled by Sass.
|
||||
>
|
||||
> More complex units involving percentages are allowed because any non-linear
|
||||
> function will throw for complex units anyway.
|
||||
|
||||
### Exact Equality
|
||||
|
||||
Two [doubles] are said to be *exactly equal* if they are equal according to the
|
||||
`compareQuietEqual` predicate as defined by [IEEE 754 2019], §5.11.
|
||||
|
||||
[doubles]: #double
|
||||
|
||||
> This is as opposed to [fuzzy equality].
|
||||
>
|
||||
> [fuzzy equality]: #fuzzy-equality
|
||||
|
||||
### Fuzzy Equality
|
||||
|
||||
Two [doubles] are said to be *fuzzy equal* to one another if either:
|
||||
|
||||
[doubles]: #double
|
||||
|
||||
* They are equal according to the `compareQuietEqual` predicate as defined
|
||||
by [IEEE 754 2019], §5.11.
|
||||
|
||||
@ -195,47 +218,28 @@ denominator*. A number that is not potentially slash-separated is known as
|
||||
*slash-free*.
|
||||
|
||||
A potentially slash-separated number is created when a `ProductExpression` with
|
||||
a `/` operator is evaluated and both operands are *syntactically* one of the
|
||||
a `/` operator is evaluated and each operand is *syntactically* one of the
|
||||
following:
|
||||
|
||||
* `Number`s,
|
||||
* [`Calculation`]s, or
|
||||
* `ProductExpression`s that can themselves create potentially slash-separated
|
||||
* a `Number`,
|
||||
* a [`FunctionCall`], or
|
||||
* a `ProductExpression` that can itself create potentially slash-separated
|
||||
numbers.
|
||||
|
||||
[`Calculation`]: calculation.md#syntax
|
||||
|
||||
If both operands are evaluated as numbers, the resulting number is potentially
|
||||
slash-separated. The first operand is the original numerator of the potentially
|
||||
slash-separated number returned by the `/` operator, and the second is the
|
||||
original denominator.
|
||||
[`FunctionCall`]: ../functions.md#functioncall
|
||||
|
||||
A potentially slash-separated number is converted to a slash-free number when:
|
||||
If the result of evaluating the `ProductExpression` is a number, that number is
|
||||
potentially slash-separated if all of the following are true:
|
||||
|
||||
* It is the value of a `ParenthesizedExpression`.
|
||||
* the results of evaluating both operands were numbers, and
|
||||
* if either operand was a `FunctionCall`, it was [evaluated as a calculation]
|
||||
and its name was not `"abs"`, `"max"`, `"min"`, or `"round"`.
|
||||
|
||||
> That is, it's in parentheses, such as in `(1 / 2)`. Note that if it's in a
|
||||
> list that's in parentheses, it's *not* converted to a slash-free number.
|
||||
[evaluated as a calculation]: calculation.md#evaluating-a-functioncall-as-a-calculation
|
||||
|
||||
* It is stored in a Sass variable.
|
||||
|
||||
* It is passed to a function or mixin.
|
||||
|
||||
* It is returned by a function.
|
||||
|
||||
> Any expressions that normally produce a new number (such as other mathematical
|
||||
> operations) always produce slash-free numbers, even when their arguments are
|
||||
> slash-separated.
|
||||
>
|
||||
> When a potentially slash-separated number is "converted" to a slash-free
|
||||
> number, a slash-free copy is made of the original. Sass values are always
|
||||
> immutable.
|
||||
|
||||
When a potentially slash-separated number is converted to CSS, either when
|
||||
converted to a string via interpolation or when included in a declaration's
|
||||
value, it is written as the original numerator followed by `/` followed by the
|
||||
original denominator. If either the original numerator or denominator are
|
||||
themselves slash-separated, they're also written this way.
|
||||
If both of these are true, the first operand is the original numerator of the
|
||||
potentially slash-separated number returned by the `/` operator, and the second
|
||||
is the original denominator.
|
||||
|
||||
## Types
|
||||
|
||||
@ -341,23 +345,29 @@ Let `n1` and `n2` be two numbers. To determine `n1 % n2`:
|
||||
* Let `c1` and `c2` be the result of [matching units] for `n1` and `n2` allowing
|
||||
unitless.
|
||||
|
||||
* If `c2` is infinity and has a different sign than `c1` (including
|
||||
oppositely-signed zero), return NaN with the same units as `c1`.
|
||||
|
||||
> This matches the behavior of CSS's `mod()` function.
|
||||
|
||||
* Let `remainder` be a number whose value is the result of `remainder(c1.value,
|
||||
c2.value)` as defined by [IEEE 754 2019], §5.3.1; and whose units are the same
|
||||
as `c1`'s.
|
||||
|
||||
* If `c2`'s value is less than 0 and `remainder`'s value isn't `0` or `-0`,
|
||||
return `result - c2`.
|
||||
* If `c2`'s value is less than 0 and `remainder`'s value isn't [exactly equal]
|
||||
to `0`, return `remainder - c2`.
|
||||
|
||||
[exactly equal]: #exact-equality
|
||||
|
||||
> This is known as [floored division]. It differs from the standard IEEE 754
|
||||
> specification because it was originally inherited from Ruby when that was
|
||||
> used for Sass's original implementation.
|
||||
> specification, but matches the behavior of CSS's `mod()` function.
|
||||
>
|
||||
> Note: These comparisons are not the same as `c2 < 0` or `remainder == 0`,
|
||||
> because they don't do fuzzy equality.
|
||||
|
||||
[floored division]: https://en.wikipedia.org/wiki/Modulo_operation#Variants_of_the_definition
|
||||
|
||||
* Otherwise, return `result`.
|
||||
* Otherwise, return `remainder`.
|
||||
|
||||
#### Negation
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user