Skip to content

Commit

Permalink
Calc round function
Browse files Browse the repository at this point in the history
  • Loading branch information
pamelalozano16 committed May 24, 2023
1 parent de889f5 commit 885dfc7
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 15 deletions.
5 changes: 5 additions & 0 deletions lib/src/ast/sass/expression/calculation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ class CalculationExpression implements Expression {
}
}

/// Returns a `round()` calculation expression.
CalculationExpression.round(
Expression strategy, Expression number, Expression step, FileSpan span)
: this("round", [strategy, number, step], span);

/// Returns a calculation expression with the given name and arguments.
///
/// Unlike the other constructors, this doesn't verify that the arguments are
Expand Down
9 changes: 1 addition & 8 deletions lib/src/functions/math.dart
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,7 @@ final _log = _function("log", r"$number, $base: null", (arguments) {
final _pow = _function("pow", r"$base, $exponent", (arguments) {
var base = arguments[0].assertNumber("base");
var exponent = arguments[1].assertNumber("exponent");
if (base.hasUnits) {
throw SassScriptException("\$base: Expected $base to have no units.");
} else if (exponent.hasUnits) {
throw SassScriptException(
"\$exponent: Expected $exponent to have no units.");
} else {
return SassNumber(math.pow(base.value, exponent.value));
}
return pow(base, exponent);
});

final _sqrt = _function("sqrt", r"$number", (arguments) {
Expand Down
1 change: 1 addition & 0 deletions lib/src/parse/stylesheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2917,6 +2917,7 @@ abstract class StylesheetParser extends Parser {
return CalculationExpression(name, arguments, scanner.spanFrom(start));

case "clamp":
case "round":
var arguments = _calculationArguments(3);
return CalculationExpression(name, arguments, scanner.spanFrom(start));

Expand Down
31 changes: 31 additions & 0 deletions lib/src/util/number.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'dart:math' as math;

import '../exception.dart';
import '../value.dart';

/// The power of ten to which to round Sass numbers to determine if they're
Expand Down Expand Up @@ -131,3 +132,33 @@ SassNumber pow(SassNumber num1, SassNumber num2) {
num2.assertNoUnits();
return SassNumber(math.pow(num1.value, num2.value));
}

/// Return the rounded number according to the strategy
SassNumber roundStrategies(SassString strategy, SassNumber number) {
if (!number.value.isNaN && number.value.isFinite) {
var strategies = {
'nearest': SassNumber(number.value.round().toDouble()),
'up': SassNumber(number.value.ceil().toDouble()),
'down': SassNumber(number.value.floor().toDouble()),
'to-zero': number.value < 0
? SassNumber(number.value.ceil().toDouble())
: SassNumber(number.value.floor().toDouble())
};

return strategies[strategy.text] ?? SassNumber(double.nan);
}
return SassNumber(double.nan);
}

/// Return the rounded number according to the strategy and the step provided.
SassNumber step(SassString strategy, SassNumber number, SassNumber step) {
var stepStrategies = {
'nearest': SassNumber((number.value / step.value).round() * step.value),
'up': SassNumber((number.value / step.value).ceil() * step.value),
'down': SassNumber((number.value / step.value).floor() * step.value),
'to-zero': number.value < 0
? SassNumber((number.value / step.value).ceil() * step.value)
: SassNumber((number.value / step.value).floor() * step.value)
};
return stepStrategies[strategy.text] ?? step;
}
79 changes: 74 additions & 5 deletions lib/src/value/calculation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
// https://opensource.org/licenses/MIT.

import 'package:meta/meta.dart';
import 'package:sass/src/ast/sass.dart';

import '../exception.dart';
import '../util/nullable.dart';
import '../util/number.dart' as number;
import '../util/number.dart' as number_lib;
import '../utils.dart';
import '../value.dart';
import '../visitor/interface/value.dart';
Expand Down Expand Up @@ -134,7 +135,7 @@ class SassCalculation extends Value {
if (argument is! SassNumber) {
return SassCalculation._("sqrt", [argument]);
}
return number.sqrt(argument);
return number_lib.sqrt(argument);
}

/// Creates a `clamp()` calculation with the given [min], [value], and [max].
Expand Down Expand Up @@ -175,7 +176,7 @@ class SassCalculation extends Value {
return SassCalculation._("clamp", args);
}

/// Creates a `pow()` calculation with the given [min], [value], and [max].
/// Creates a `pow()` calculation with the given [arguments].
///
/// Each argument must be either a [SassNumber], a [SassCalculation], an
/// unquoted [SassString], a [CalculationOperation], or a
Expand All @@ -196,7 +197,75 @@ class SassCalculation extends Value {
return SassCalculation._("pow", [num1, num2]);
}

return number.pow(num1, num2);
return number_lib.pow(num1, num2);
}

/// Creates a `round()` calculation with the given [strategy], [number], and [step].
///
/// If a step is given the strategy is required.
/// Strategy must be either nearest, up, down or to-zero.
///
/// Number and step must be either a [SassNumber], a [SassCalculation], an
/// unquoted [SassString], a [CalculationOperation], or a
/// [CalculationInterpolation].
///
/// This automatically simplifies the calculation, so it may return a
/// [SassNumber] rather than a [SassCalculation]. It throws an exception if it
/// can determine that the calculation will definitely produce invalid CSS.
///
/// This may be passed fewer than three arguments, but only if one of the
/// arguments is an unquoted `var()` string.
static Value round(Object? strategy, Object number, Object? step) {
number = _simplify(number);
step = step.andThen(_simplify);

var args = [if (strategy != null) strategy, number, if (step != null) step];

//Set default strategy
strategy ??= SassString('nearest', quotes: false);
if (strategy is! SassString ||
!{'nearest', 'up', 'down', 'to-zero'}.contains(strategy.text)) {
if (step == null && strategy is SassNumber) {
throw SassScriptException("If step not null, strategy is required.");
}
throw SassScriptException(
"$strategy must be either nearest, up, down or to-zero.");
}

//Handle calculations
if (number is! SassNumber || (step != null && step is! SassNumber)) {
return SassCalculation._("round", args);
}

if (step is SassNumber) {
if (step.value == 0) return SassNumber(double.nan);
if (number.value.isInfinite) {
if (step.value.isInfinite) {
return SassNumber(double.nan);
}
return SassNumber(double.infinity);
}

if (step.value.isInfinite) {
if (number.value == -0) return number;
if (number.value >= 0 && strategy.text == "up") {
return SassNumber(double.infinity);
}
if (number.value < 0 && strategy.text == "down") {
return SassNumber(-double.infinity);
}

return SassNumber(0);
}

_verifyCompatibleNumbers([number, step]);

//Handle step
return number_lib.step(strategy, number, step);
} else if (step == null) {
return number_lib.roundStrategies(strategy, number);
}
return SassNumber(double.nan);
}

/// Creates and simplifies a [CalculationOperation] with the given [operator],
Expand Down Expand Up @@ -243,7 +312,7 @@ class SassCalculation extends Value {

_verifyCompatibleNumbers([left, right]);

if (right is SassNumber && number.fuzzyLessThan(right.value, 0)) {
if (right is SassNumber && number_lib.fuzzyLessThan(right.value, 0)) {
right = right.times(SassNumber(-1));
operator = operator == CalculationOperator.plus
? CalculationOperator.minus
Expand Down
10 changes: 9 additions & 1 deletion lib/src/visitor/async_evaluate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2292,7 +2292,7 @@ class _EvaluateVisitor
var arguments = [
for (var argument in node.arguments)
await _visitCalculationValue(argument,
inMinMax: node.name == 'min' || node.name == 'max')
inMinMax: {'min', 'max', 'round'}.contains(node.name))
];
if (_inSupportsDeclaration) {
return SassCalculation.unsimplified(node.name, arguments);
Expand All @@ -2312,6 +2312,14 @@ class _EvaluateVisitor
return SassCalculation.max(arguments);
case "pow":
return SassCalculation.pow(arguments);
case "round":
assert(arguments.isNotEmpty, true);
return arguments.length > 2
? SassCalculation.round(arguments[0], arguments[1], arguments[2])
: SassCalculation.round(
arguments.length > 1 ? arguments[0] : null,
arguments.length == 1 ? arguments[0] : arguments[1],
null);
case "clamp":
return SassCalculation.clamp(
arguments[0],
Expand Down
10 changes: 9 additions & 1 deletion lib/src/visitor/evaluate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2281,7 +2281,7 @@ class _EvaluateVisitor
var arguments = [
for (var argument in node.arguments)
_visitCalculationValue(argument,
inMinMax: node.name == 'min' || node.name == 'max')
inMinMax: {'min', 'max', 'round'}.contains(node.name))
];
if (_inSupportsDeclaration) {
return SassCalculation.unsimplified(node.name, arguments);
Expand All @@ -2301,6 +2301,14 @@ class _EvaluateVisitor
return SassCalculation.max(arguments);
case "pow":
return SassCalculation.pow(arguments);
case "round":
assert(arguments.isNotEmpty, true);
return arguments.length > 2
? SassCalculation.round(arguments[0], arguments[1], arguments[2])
: SassCalculation.round(
arguments.length > 1 ? arguments[0] : null,
arguments.length == 1 ? arguments[0] : arguments[1],
null);
case "clamp":
return SassCalculation.clamp(
arguments[0],
Expand Down

0 comments on commit 885dfc7

Please sign in to comment.