Skip to content

Commit 6a7a1d6

Browse files
committed
Fix math with multiple units
1 parent 6c10b38 commit 6a7a1d6

File tree

9 files changed

+631
-532
lines changed

9 files changed

+631
-532
lines changed

src/ast.cpp

Lines changed: 40 additions & 352 deletions
Large diffs are not rendered by default.

src/ast.hpp

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ namespace Sass {
7575
// Note: most methods follow precision option
7676
const double NUMBER_EPSILON = 0.00000000000001;
7777

78+
// macro to test if numbers are equal within a small error margin
79+
#define NEAR_EQUAL(lhs, rhs) std::fabs(lhs - rhs) < NUMBER_EPSILON
80+
7881
// ToDo: where does this fit best?
7982
// We don't share this with C-API?
8083
class Operand {
@@ -1594,53 +1597,41 @@ namespace Sass {
15941597
////////////////////////////////////////////////
15951598
// Numbers, percentages, dimensions, and colors.
15961599
////////////////////////////////////////////////
1597-
class Number : public Value {
1600+
class Number : public Value, public Units {
15981601
HASH_PROPERTY(double, value)
15991602
ADD_PROPERTY(bool, zero)
1600-
std::vector<std::string> numerator_units_;
1601-
std::vector<std::string> denominator_units_;
16021603
size_t hash_;
16031604
public:
16041605
Number(ParserState pstate, double val, std::string u = "", bool zero = true);
16051606

16061607
Number(const Number* ptr)
16071608
: Value(ptr),
1609+
Units(ptr),
16081610
value_(ptr->value_), zero_(ptr->zero_),
1609-
numerator_units_(ptr->numerator_units_),
1610-
denominator_units_(ptr->denominator_units_),
16111611
hash_(ptr->hash_)
16121612
{ concrete_type(NUMBER); }
16131613

16141614
bool zero() { return zero_; }
1615-
bool is_valid_css_unit() const;
1616-
std::vector<std::string>& numerator_units() { return numerator_units_; }
1617-
std::vector<std::string>& denominator_units() { return denominator_units_; }
1618-
const std::vector<std::string>& numerator_units() const { return numerator_units_; }
1619-
const std::vector<std::string>& denominator_units() const { return denominator_units_; }
16201615
std::string type() const { return "number"; }
16211616
static std::string type_name() { return "number"; }
1622-
std::string unit() const;
16231617

1624-
bool is_unitless() const;
1625-
double convert_factor(const Number&) const;
1626-
bool convert(const std::string& unit = "", bool strict = false);
1627-
void normalize(const std::string& unit = "", bool strict = false);
1628-
// useful for making one number compatible with another
1629-
std::string find_convertible_unit() const;
1618+
void reduce();
1619+
void normalize();
16301620

16311621
virtual size_t hash()
16321622
{
16331623
if (hash_ == 0) {
16341624
hash_ = std::hash<double>()(value_);
1635-
for (const auto numerator : numerator_units())
1625+
for (const auto numerator : numerators)
16361626
hash_combine(hash_, std::hash<std::string>()(numerator));
1637-
for (const auto denominator : denominator_units())
1627+
for (const auto denominator : denominators)
16381628
hash_combine(hash_, std::hash<std::string>()(denominator));
16391629
}
16401630
return hash_;
16411631
}
16421632

16431633
virtual bool operator< (const Number& rhs) const;
1634+
virtual bool operator== (const Number& rhs) const;
16441635
virtual bool operator== (const Expression& rhs) const;
16451636
ATTACH_AST_OPERATIONS(Number)
16461637
ATTACH_OPERATIONS()

src/error_handling.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ namespace Sass {
121121
msg = "stack level too deep";
122122
}
123123

124-
IncompatibleUnits::IncompatibleUnits(const Number& lhs, const Number& rhs)
124+
IncompatibleUnits::IncompatibleUnits(const Units& lhs, const Units& rhs)
125125
{
126126
msg = "Incompatible units: '";
127127
msg += rhs.unit();

src/error_handling.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ namespace Sass {
157157
// const Sass::UnitType lhs;
158158
// const Sass::UnitType rhs;
159159
public:
160-
IncompatibleUnits(const Number& lhs, const Number& rhs);
160+
IncompatibleUnits(const Units& lhs, const Units& rhs);
161161
IncompatibleUnits(const UnitType lhs, const UnitType rhs);
162162
virtual ~IncompatibleUnits() throw() {};
163163
};

src/eval.cpp

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,7 @@ namespace Sass {
812812
if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {
813813
Number_Ptr l_n = Cast<Number>(lhs);
814814
Number_Ptr r_n = Cast<Number>(rhs);
815+
l_n->reduce(); r_n->reduce();
815816
rv = op_numbers(op_type, *l_n, *r_n, ctx.c_options, pstate);
816817
}
817818
else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {
@@ -1499,24 +1500,26 @@ namespace Sass {
14991500
{
15001501
double lv = l.value();
15011502
double rv = r.value();
1503+
15021504
if (op == Sass_OP::DIV && rv == 0) {
15031505
// XXX: this is never hit via spec tests
15041506
return SASS_MEMORY_NEW(String_Quoted, pstate, lv ? "Infinity" : "NaN");
15051507
}
1508+
15061509
if (op == Sass_OP::MOD && !rv) {
15071510
// XXX: this is never hit via spec tests
15081511
throw Exception::ZeroDivisionError(l, r);
15091512
}
15101513

1511-
size_t l_n_units = l.numerator_units().size();
1512-
size_t l_d_units = l.numerator_units().size();
1513-
size_t r_n_units = r.denominator_units().size();
1514-
size_t r_d_units = r.denominator_units().size();
1514+
size_t l_n_units = l.numerators.size();
1515+
size_t l_d_units = l.numerators.size();
1516+
size_t r_n_units = r.denominators.size();
1517+
size_t r_d_units = r.denominators.size();
15151518
// optimize out the most common and simplest case
15161519
if (l_n_units == r_n_units && l_d_units == r_d_units) {
15171520
if (l_n_units + l_d_units <= 1 && r_n_units + r_d_units <= 1) {
1518-
if (l.numerator_units() == r.numerator_units()) {
1519-
if (l.denominator_units() == r.denominator_units()) {
1521+
if (l.numerators == r.numerators) {
1522+
if (l.denominators == r.denominators) {
15201523
Number_Ptr v = SASS_MEMORY_COPY(&l);
15211524
v->value(ops[op](lv, rv));
15221525
return v;
@@ -1525,41 +1528,39 @@ namespace Sass {
15251528
}
15261529
}
15271530

1528-
Number tmp(&r); // copy
1529-
bool strict = op != Sass_OP::MUL && op != Sass_OP::DIV;
1530-
tmp.normalize(l.find_convertible_unit(), strict);
1531-
std::string l_unit(l.unit());
1532-
std::string r_unit(tmp.unit());
1533-
Number_Obj v = SASS_MEMORY_COPY(&l); // copy
1534-
v->pstate(pstate);
1535-
if (l_unit.empty() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {
1536-
v->numerator_units() = r.numerator_units();
1537-
v->denominator_units() = r.denominator_units();
1531+
Number_Obj v = SASS_MEMORY_COPY(&l);
1532+
1533+
if (l.is_unitless() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {
1534+
v->numerators = r.numerators;
1535+
v->denominators = r.denominators;
15381536
}
15391537

15401538
if (op == Sass_OP::MUL) {
15411539
v->value(ops[op](lv, rv));
1542-
for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) {
1543-
v->numerator_units().push_back(r.numerator_units()[i]);
1544-
}
1545-
for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) {
1546-
v->denominator_units().push_back(r.denominator_units()[i]);
1547-
}
1540+
v->numerators.insert(v->numerators.end(),
1541+
r.numerators.begin(), r.numerators.end()
1542+
);
1543+
v->denominators.insert(v->denominators.end(),
1544+
r.denominators.begin(), r.denominators.end()
1545+
);
15481546
}
15491547
else if (op == Sass_OP::DIV) {
15501548
v->value(ops[op](lv, rv));
1551-
for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) {
1552-
v->denominator_units().push_back(r.numerator_units()[i]);
1553-
}
1554-
for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) {
1555-
v->numerator_units().push_back(r.denominator_units()[i]);
1556-
}
1557-
} else {
1558-
v->value(ops[op](lv, r.value() * r.convert_factor(l)));
1559-
// v->normalize();
1560-
return v.detach();
1549+
v->numerators.insert(v->numerators.end(),
1550+
r.denominators.begin(), r.denominators.end()
1551+
);
1552+
v->denominators.insert(v->denominators.end(),
1553+
r.numerators.begin(), r.numerators.end()
1554+
);
1555+
}
1556+
else {
1557+
Number ln(l), rn(r);
1558+
ln.reduce(); rn.reduce();
1559+
double f(rn.convert_factor(ln));
1560+
v->value(ops[op](lv, rn.value() * f));
15611561
}
1562-
v->normalize();
1562+
1563+
v->pstate(pstate);
15631564
return v.detach();
15641565
}
15651566

0 commit comments

Comments
 (0)