sassc 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/ext/libsass/Readme.md +1 -1
- data/ext/libsass/ast.cpp +264 -0
- data/ext/libsass/ast.hpp +52 -133
- data/ext/libsass/bind.cpp +23 -1
- data/ext/libsass/constants.cpp +3 -1
- data/ext/libsass/constants.hpp +3 -0
- data/ext/libsass/context.cpp +0 -1
- data/ext/libsass/debugger.hpp +84 -2
- data/ext/libsass/error_handling.cpp +14 -4
- data/ext/libsass/error_handling.hpp +3 -0
- data/ext/libsass/eval.cpp +44 -14
- data/ext/libsass/functions.cpp +37 -37
- data/ext/libsass/functions.hpp +0 -2
- data/ext/libsass/inspect.cpp +9 -2
- data/ext/libsass/output.cpp +7 -14
- data/ext/libsass/output.hpp +1 -2
- data/ext/libsass/parser.cpp +16 -4
- data/ext/libsass/position.hpp +5 -0
- data/ext/libsass/prelexer.cpp +5 -1
- data/ext/libsass/sass_values.cpp +15 -0
- data/ext/libsass/sass_values.h +3 -0
- data/ext/libsass/util.cpp +63 -7
- data/ext/libsass/util.hpp +2 -0
- data/lib/sassc/engine.rb +2 -0
- data/lib/sassc/native.rb +1 -1
- data/lib/sassc/version.rb +1 -1
- data/test/engine_test.rb +7 -0
- data/test/native_test.rb +10 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cba905c4a611564a30f673941af311b360e64f79
|
|
4
|
+
data.tar.gz: 83bfb6df3c6ebb5e086badf0a950ebc839cbc6f7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bd8dbb02e65de541608e51da7738824d1de7a8db2429d4b05396173a861ec2bbcd4d191ab39b426427b7ed5f31bcacf94a22782295827d736b1c0c1e8bd44212
|
|
7
|
+
data.tar.gz: e6c839d26054ff6bba74ab525e357f3161a25416748baacc0a2abd416ad21d0a9dafeb6d8d038e8a929d08eac12e28a53d109b0a2aaa4e4902b89abd715be1ac
|
data/README.md
CHANGED
data/ext/libsass/Readme.md
CHANGED
|
@@ -7,7 +7,7 @@ by Aaron Leung ([@akhleung]) and Hampton Catlin ([@hcatlin])
|
|
|
7
7
|
[](https://ci.appveyor.com/project/sass/libsass/branch/master)
|
|
8
8
|
[](https://www.bountysource.com/trackers/283068-libsass?utm_source=283068&utm_medium=shield&utm_campaign=TRACKER_BADGE)
|
|
9
9
|
[](https://coveralls.io/r/sass/libsass?branch=feature%2Ftest-travis-ci-3)
|
|
10
|
-
[](https://libsass-slack.herokuapp.com/)
|
|
11
11
|
|
|
12
12
|
https://github.com/sass/libsass
|
|
13
13
|
|
data/ext/libsass/ast.cpp
CHANGED
|
@@ -589,6 +589,270 @@ namespace Sass {
|
|
|
589
589
|
return result;
|
|
590
590
|
}*/
|
|
591
591
|
|
|
592
|
+
Number::Number(ParserState pstate, double val, string u, bool zero)
|
|
593
|
+
: Expression(pstate),
|
|
594
|
+
value_(val),
|
|
595
|
+
zero_(zero),
|
|
596
|
+
numerator_units_(vector<string>()),
|
|
597
|
+
denominator_units_(vector<string>()),
|
|
598
|
+
hash_(0)
|
|
599
|
+
{
|
|
600
|
+
size_t l = 0, r = 0;
|
|
601
|
+
if (!u.empty()) {
|
|
602
|
+
bool nominator = true;
|
|
603
|
+
while (true) {
|
|
604
|
+
r = u.find_first_of("*/", l);
|
|
605
|
+
string unit(u.substr(l, r - l));
|
|
606
|
+
if (nominator) numerator_units_.push_back(unit);
|
|
607
|
+
else denominator_units_.push_back(unit);
|
|
608
|
+
if (u[r] == '/') nominator = false;
|
|
609
|
+
if (r == string::npos) break;
|
|
610
|
+
else l = r + 1;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
concrete_type(NUMBER);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
string Number::unit() const
|
|
617
|
+
{
|
|
618
|
+
stringstream u;
|
|
619
|
+
for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {
|
|
620
|
+
if (i) u << '*';
|
|
621
|
+
u << numerator_units_[i];
|
|
622
|
+
}
|
|
623
|
+
if (!denominator_units_.empty()) u << '/';
|
|
624
|
+
for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {
|
|
625
|
+
if (i) u << '*';
|
|
626
|
+
u << denominator_units_[i];
|
|
627
|
+
}
|
|
628
|
+
return u.str();
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
bool Number::is_unitless()
|
|
632
|
+
{ return numerator_units_.empty() && denominator_units_.empty(); }
|
|
633
|
+
|
|
634
|
+
void Number::normalize(const string& prefered)
|
|
635
|
+
{
|
|
636
|
+
|
|
637
|
+
// first make sure same units cancel each other out
|
|
638
|
+
// it seems that a map table will fit nicely to do this
|
|
639
|
+
// we basically construct exponents for each unit
|
|
640
|
+
// has the advantage that they will be pre-sorted
|
|
641
|
+
map<string, int> exponents;
|
|
642
|
+
|
|
643
|
+
// initialize by summing up occurences in unit vectors
|
|
644
|
+
for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];
|
|
645
|
+
for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];
|
|
646
|
+
|
|
647
|
+
// the final conversion factor
|
|
648
|
+
double factor = 1;
|
|
649
|
+
|
|
650
|
+
// get the first entry of numerators
|
|
651
|
+
// forward it when entry is converted
|
|
652
|
+
vector<string>::iterator nom_it = numerator_units_.begin();
|
|
653
|
+
vector<string>::iterator nom_end = numerator_units_.end();
|
|
654
|
+
vector<string>::iterator denom_it = denominator_units_.begin();
|
|
655
|
+
vector<string>::iterator denom_end = denominator_units_.end();
|
|
656
|
+
|
|
657
|
+
// main normalization loop
|
|
658
|
+
// should be close to optimal
|
|
659
|
+
while (denom_it != denom_end)
|
|
660
|
+
{
|
|
661
|
+
// get and increment afterwards
|
|
662
|
+
const string denom = *(denom_it ++);
|
|
663
|
+
// skip already canceled out unit
|
|
664
|
+
if (exponents[denom] >= 0) continue;
|
|
665
|
+
// skip all units we don't know how to convert
|
|
666
|
+
if (string_to_unit(denom) == INCOMMENSURABLE) continue;
|
|
667
|
+
// now search for nominator
|
|
668
|
+
while (nom_it != nom_end)
|
|
669
|
+
{
|
|
670
|
+
// get and increment afterwards
|
|
671
|
+
const string nom = *(nom_it ++);
|
|
672
|
+
// skip already canceled out unit
|
|
673
|
+
if (exponents[nom] <= 0) continue;
|
|
674
|
+
// skip all units we don't know how to convert
|
|
675
|
+
if (string_to_unit(nom) == INCOMMENSURABLE) continue;
|
|
676
|
+
// we now have two convertable units
|
|
677
|
+
// add factor for current conversion
|
|
678
|
+
factor *= conversion_factor(nom, denom);
|
|
679
|
+
// update nominator/denominator exponent
|
|
680
|
+
-- exponents[nom]; ++ exponents[denom];
|
|
681
|
+
// inner loop done
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// now we can build up the new unit arrays
|
|
687
|
+
numerator_units_.clear();
|
|
688
|
+
denominator_units_.clear();
|
|
689
|
+
|
|
690
|
+
// build them by iterating over the exponents
|
|
691
|
+
for (auto exp : exponents)
|
|
692
|
+
{
|
|
693
|
+
// maybe there is more effecient way to push
|
|
694
|
+
// the same item multiple times to a vector?
|
|
695
|
+
for(size_t i = 0, S = abs(exp.second); i < S; ++i)
|
|
696
|
+
{
|
|
697
|
+
// opted to have these switches in the inner loop
|
|
698
|
+
// makes it more readable and should not cost much
|
|
699
|
+
if (exp.second < 0) denominator_units_.push_back(exp.first);
|
|
700
|
+
else if (exp.second > 0) numerator_units_.push_back(exp.first);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// apply factor to value_
|
|
705
|
+
// best precision this way
|
|
706
|
+
value_ *= factor;
|
|
707
|
+
|
|
708
|
+
// maybe convert to other unit
|
|
709
|
+
// easier implemented on its own
|
|
710
|
+
convert(prefered);
|
|
711
|
+
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
void Number::convert(const string& prefered)
|
|
715
|
+
{
|
|
716
|
+
// abort if unit is empty
|
|
717
|
+
if (prefered.empty()) return;
|
|
718
|
+
|
|
719
|
+
// first make sure same units cancel each other out
|
|
720
|
+
// it seems that a map table will fit nicely to do this
|
|
721
|
+
// we basically construct exponents for each unit
|
|
722
|
+
// has the advantage that they will be pre-sorted
|
|
723
|
+
map<string, int> exponents;
|
|
724
|
+
|
|
725
|
+
// initialize by summing up occurences in unit vectors
|
|
726
|
+
for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];
|
|
727
|
+
for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];
|
|
728
|
+
|
|
729
|
+
// the final conversion factor
|
|
730
|
+
double factor = 1;
|
|
731
|
+
|
|
732
|
+
vector<string>::iterator denom_it = denominator_units_.begin();
|
|
733
|
+
vector<string>::iterator denom_end = denominator_units_.end();
|
|
734
|
+
|
|
735
|
+
// main normalization loop
|
|
736
|
+
// should be close to optimal
|
|
737
|
+
while (denom_it != denom_end)
|
|
738
|
+
{
|
|
739
|
+
// get and increment afterwards
|
|
740
|
+
const string denom = *(denom_it ++);
|
|
741
|
+
// check if conversion is needed
|
|
742
|
+
if (denom == prefered) continue;
|
|
743
|
+
// skip already canceled out unit
|
|
744
|
+
if (exponents[denom] >= 0) continue;
|
|
745
|
+
// skip all units we don't know how to convert
|
|
746
|
+
if (string_to_unit(denom) == INCOMMENSURABLE) continue;
|
|
747
|
+
// we now have two convertable units
|
|
748
|
+
// add factor for current conversion
|
|
749
|
+
factor *= conversion_factor(denom, prefered);
|
|
750
|
+
// update nominator/denominator exponent
|
|
751
|
+
++ exponents[denom]; -- exponents[prefered];
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
vector<string>::iterator nom_it = numerator_units_.begin();
|
|
755
|
+
vector<string>::iterator nom_end = numerator_units_.end();
|
|
756
|
+
|
|
757
|
+
// now search for nominator
|
|
758
|
+
while (nom_it != nom_end)
|
|
759
|
+
{
|
|
760
|
+
// get and increment afterwards
|
|
761
|
+
const string nom = *(nom_it ++);
|
|
762
|
+
// check if conversion is needed
|
|
763
|
+
if (nom == prefered) continue;
|
|
764
|
+
// skip already canceled out unit
|
|
765
|
+
if (exponents[nom] <= 0) continue;
|
|
766
|
+
// skip all units we don't know how to convert
|
|
767
|
+
if (string_to_unit(nom) == INCOMMENSURABLE) continue;
|
|
768
|
+
// we now have two convertable units
|
|
769
|
+
// add factor for current conversion
|
|
770
|
+
factor *= conversion_factor(nom, prefered);
|
|
771
|
+
// update nominator/denominator exponent
|
|
772
|
+
-- exponents[nom]; ++ exponents[prefered];
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// now we can build up the new unit arrays
|
|
776
|
+
numerator_units_.clear();
|
|
777
|
+
denominator_units_.clear();
|
|
778
|
+
|
|
779
|
+
// build them by iterating over the exponents
|
|
780
|
+
for (auto exp : exponents)
|
|
781
|
+
{
|
|
782
|
+
// maybe there is more effecient way to push
|
|
783
|
+
// the same item multiple times to a vector?
|
|
784
|
+
for(size_t i = 0, S = abs(exp.second); i < S; ++i)
|
|
785
|
+
{
|
|
786
|
+
// opted to have these switches in the inner loop
|
|
787
|
+
// makes it more readable and should not cost much
|
|
788
|
+
if (exp.second < 0) denominator_units_.push_back(exp.first);
|
|
789
|
+
else if (exp.second > 0) numerator_units_.push_back(exp.first);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// apply factor to value_
|
|
794
|
+
// best precision this way
|
|
795
|
+
value_ *= factor;
|
|
796
|
+
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// useful for making one number compatible with another
|
|
800
|
+
string Number::find_convertible_unit() const
|
|
801
|
+
{
|
|
802
|
+
for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {
|
|
803
|
+
string u(numerator_units_[i]);
|
|
804
|
+
if (string_to_unit(u) != INCOMMENSURABLE) return u;
|
|
805
|
+
}
|
|
806
|
+
for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {
|
|
807
|
+
string u(denominator_units_[i]);
|
|
808
|
+
if (string_to_unit(u) != INCOMMENSURABLE) return u;
|
|
809
|
+
}
|
|
810
|
+
return string();
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
bool Number::operator== (Expression* rhs) const
|
|
815
|
+
{
|
|
816
|
+
try
|
|
817
|
+
{
|
|
818
|
+
Number l(pstate_, value_, unit());
|
|
819
|
+
Number& r = dynamic_cast<Number&>(*rhs);
|
|
820
|
+
l.normalize(find_convertible_unit());
|
|
821
|
+
r.normalize(find_convertible_unit());
|
|
822
|
+
return l.unit() == r.unit() &&
|
|
823
|
+
l.value() == r.value();
|
|
824
|
+
}
|
|
825
|
+
catch (std::bad_cast&) {}
|
|
826
|
+
catch (...) { throw; }
|
|
827
|
+
return false;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
bool Number::operator== (Expression& rhs) const
|
|
831
|
+
{
|
|
832
|
+
return operator==(&rhs);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
bool List::operator==(Expression* rhs) const
|
|
836
|
+
{
|
|
837
|
+
try
|
|
838
|
+
{
|
|
839
|
+
List* r = dynamic_cast<List*>(rhs);
|
|
840
|
+
if (!r || length() != r->length()) return false;
|
|
841
|
+
if (separator() != r->separator()) return false;
|
|
842
|
+
for (size_t i = 0, L = r->length(); i < L; ++i)
|
|
843
|
+
if (*elements()[i] != *(*r)[i]) return false;
|
|
844
|
+
return true;
|
|
845
|
+
}
|
|
846
|
+
catch (std::bad_cast&) {}
|
|
847
|
+
catch (...) { throw; }
|
|
848
|
+
return false;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
bool List::operator== (Expression& rhs) const
|
|
852
|
+
{
|
|
853
|
+
return operator==(&rhs);
|
|
854
|
+
}
|
|
855
|
+
|
|
592
856
|
Expression* Hashed::at(Expression* k) const
|
|
593
857
|
{
|
|
594
858
|
if (elements_.count(k))
|
data/ext/libsass/ast.hpp
CHANGED
|
@@ -113,6 +113,7 @@ namespace Sass {
|
|
|
113
113
|
static string type_name() { return ""; }
|
|
114
114
|
virtual bool is_false() { return false; }
|
|
115
115
|
virtual bool operator==( Expression& rhs) const { return false; }
|
|
116
|
+
virtual void set_delayed(bool delayed) { is_delayed(delayed); }
|
|
116
117
|
virtual size_t hash() { return 0; }
|
|
117
118
|
};
|
|
118
119
|
}
|
|
@@ -756,23 +757,8 @@ namespace Sass {
|
|
|
756
757
|
bool is_invisible() { return !length(); }
|
|
757
758
|
Expression* value_at_index(size_t i);
|
|
758
759
|
|
|
759
|
-
virtual bool operator==(Expression& rhs) const
|
|
760
|
-
|
|
761
|
-
try
|
|
762
|
-
{
|
|
763
|
-
List& l = dynamic_cast<List&>(rhs);
|
|
764
|
-
if (!(l && length() == l.length() && separator() == l.separator())) return false;
|
|
765
|
-
for (size_t i = 0, L = l.length(); i < L; ++i)
|
|
766
|
-
if (!(*(elements()[i]) == *(l[i]))) return false;
|
|
767
|
-
return true;
|
|
768
|
-
}
|
|
769
|
-
catch (std::bad_cast&)
|
|
770
|
-
{
|
|
771
|
-
return false;
|
|
772
|
-
}
|
|
773
|
-
catch (...) { throw; }
|
|
774
|
-
|
|
775
|
-
}
|
|
760
|
+
virtual bool operator==(Expression& rhs) const;
|
|
761
|
+
virtual bool operator==(Expression* rhs) const;
|
|
776
762
|
|
|
777
763
|
virtual size_t hash()
|
|
778
764
|
{
|
|
@@ -786,6 +772,13 @@ namespace Sass {
|
|
|
786
772
|
return hash_;
|
|
787
773
|
}
|
|
788
774
|
|
|
775
|
+
virtual void set_delayed(bool delayed)
|
|
776
|
+
{
|
|
777
|
+
for (size_t i = 0, L = length(); i < L; ++i)
|
|
778
|
+
(elements()[i])->set_delayed(delayed);
|
|
779
|
+
is_delayed(delayed);
|
|
780
|
+
}
|
|
781
|
+
|
|
789
782
|
ATTACH_OPERATIONS();
|
|
790
783
|
};
|
|
791
784
|
|
|
@@ -857,6 +850,31 @@ namespace Sass {
|
|
|
857
850
|
Type t, Expression* lhs, Expression* rhs)
|
|
858
851
|
: Expression(pstate), type_(t), left_(lhs), right_(rhs), hash_(0)
|
|
859
852
|
{ }
|
|
853
|
+
const string type_name() {
|
|
854
|
+
switch (type_) {
|
|
855
|
+
case AND: return "and"; break;
|
|
856
|
+
case OR: return "or"; break;
|
|
857
|
+
case EQ: return "eq"; break;
|
|
858
|
+
case NEQ: return "neq"; break;
|
|
859
|
+
case GT: return "gt"; break;
|
|
860
|
+
case GTE: return "gte"; break;
|
|
861
|
+
case LT: return "lt"; break;
|
|
862
|
+
case LTE: return "lte"; break;
|
|
863
|
+
case ADD: return "add"; break;
|
|
864
|
+
case SUB: return "sub"; break;
|
|
865
|
+
case MUL: return "mul"; break;
|
|
866
|
+
case DIV: return "div"; break;
|
|
867
|
+
case MOD: return "mod"; break;
|
|
868
|
+
case NUM_OPS: return "num_ops"; break;
|
|
869
|
+
default: return "invalid"; break;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
virtual void set_delayed(bool delayed)
|
|
873
|
+
{
|
|
874
|
+
right()->set_delayed(delayed);
|
|
875
|
+
left()->set_delayed(delayed);
|
|
876
|
+
is_delayed(delayed);
|
|
877
|
+
}
|
|
860
878
|
virtual bool operator==(Expression& rhs) const
|
|
861
879
|
{
|
|
862
880
|
try
|
|
@@ -896,6 +914,14 @@ namespace Sass {
|
|
|
896
914
|
Unary_Expression(ParserState pstate, Type t, Expression* o)
|
|
897
915
|
: Expression(pstate), type_(t), operand_(o), hash_(0)
|
|
898
916
|
{ }
|
|
917
|
+
const string type_name() {
|
|
918
|
+
switch (type_) {
|
|
919
|
+
case PLUS: return "plus"; break;
|
|
920
|
+
case MINUS: return "minus"; break;
|
|
921
|
+
case NOT: return "not"; break;
|
|
922
|
+
default: return "invalid"; break;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
899
925
|
virtual bool operator==(Expression& rhs) const
|
|
900
926
|
{
|
|
901
927
|
try
|
|
@@ -1161,129 +1187,22 @@ namespace Sass {
|
|
|
1161
1187
|
vector<string> denominator_units_;
|
|
1162
1188
|
size_t hash_;
|
|
1163
1189
|
public:
|
|
1164
|
-
Number(ParserState pstate, double val, string u = "", bool zero = true)
|
|
1165
|
-
: Expression(pstate),
|
|
1166
|
-
value_(val),
|
|
1167
|
-
zero_(zero),
|
|
1168
|
-
numerator_units_(vector<string>()),
|
|
1169
|
-
denominator_units_(vector<string>()),
|
|
1170
|
-
hash_(0)
|
|
1171
|
-
{
|
|
1172
|
-
if (!u.empty()) numerator_units_.push_back(u);
|
|
1173
|
-
concrete_type(NUMBER);
|
|
1174
|
-
}
|
|
1190
|
+
Number(ParserState pstate, double val, string u = "", bool zero = true);
|
|
1175
1191
|
bool zero() { return zero_; }
|
|
1176
1192
|
vector<string>& numerator_units() { return numerator_units_; }
|
|
1177
1193
|
vector<string>& denominator_units() { return denominator_units_; }
|
|
1178
1194
|
string type() { return "number"; }
|
|
1179
1195
|
static string type_name() { return "number"; }
|
|
1180
|
-
string unit() const
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
u << numerator_units_[i];
|
|
1186
|
-
}
|
|
1187
|
-
if (!denominator_units_.empty()) u << '/';
|
|
1188
|
-
for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {
|
|
1189
|
-
if (i) u << '*';
|
|
1190
|
-
u << denominator_units_[i];
|
|
1191
|
-
}
|
|
1192
|
-
return u.str();
|
|
1193
|
-
}
|
|
1194
|
-
bool is_unitless()
|
|
1195
|
-
{ return numerator_units_.empty() && denominator_units_.empty(); }
|
|
1196
|
-
void normalize(string to = "")
|
|
1197
|
-
{
|
|
1198
|
-
// (multiple passes because I'm too tired to think up something clever)
|
|
1199
|
-
// Find a unit to convert everything to, if one isn't provided.
|
|
1200
|
-
if (to.empty()) {
|
|
1201
|
-
for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {
|
|
1202
|
-
string u(numerator_units_[i]);
|
|
1203
|
-
if (string_to_unit(u) == INCOMMENSURABLE) {
|
|
1204
|
-
continue;
|
|
1205
|
-
}
|
|
1206
|
-
else {
|
|
1207
|
-
to = u;
|
|
1208
|
-
break;
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
if (to.empty()) {
|
|
1213
|
-
for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {
|
|
1214
|
-
string u(denominator_units_[i]);
|
|
1215
|
-
if (string_to_unit(u) == INCOMMENSURABLE) {
|
|
1216
|
-
continue;
|
|
1217
|
-
}
|
|
1218
|
-
else {
|
|
1219
|
-
to = u;
|
|
1220
|
-
break;
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
// Now loop through again and do all the conversions.
|
|
1225
|
-
for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {
|
|
1226
|
-
string from(numerator_units_[i]);
|
|
1227
|
-
if (string_to_unit(from) == INCOMMENSURABLE) continue;
|
|
1228
|
-
value_ *= conversion_factor(from, to);
|
|
1229
|
-
numerator_units_[i] = to;
|
|
1230
|
-
}
|
|
1231
|
-
for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {
|
|
1232
|
-
string from(denominator_units_[i]);
|
|
1233
|
-
if (string_to_unit(from) == INCOMMENSURABLE) continue;
|
|
1234
|
-
value_ /= conversion_factor(from, to);
|
|
1235
|
-
denominator_units_[i] = to;
|
|
1236
|
-
}
|
|
1237
|
-
// Now divide out identical units in the numerator and denominator.
|
|
1238
|
-
vector<string> ncopy;
|
|
1239
|
-
ncopy.reserve(numerator_units_.size());
|
|
1240
|
-
for (vector<string>::iterator n = numerator_units_.begin();
|
|
1241
|
-
n != numerator_units_.end();
|
|
1242
|
-
++n) {
|
|
1243
|
-
vector<string>::iterator d = find(denominator_units_.begin(),
|
|
1244
|
-
denominator_units_.end(),
|
|
1245
|
-
*n);
|
|
1246
|
-
if (d != denominator_units_.end()) {
|
|
1247
|
-
denominator_units_.erase(d);
|
|
1248
|
-
}
|
|
1249
|
-
else {
|
|
1250
|
-
ncopy.push_back(*n);
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
numerator_units_ = ncopy;
|
|
1254
|
-
// Sort the units to make them pretty and, well, normal.
|
|
1255
|
-
sort(numerator_units_.begin(), numerator_units_.end());
|
|
1256
|
-
sort(denominator_units_.begin(), denominator_units_.end());
|
|
1257
|
-
}
|
|
1196
|
+
string unit() const;
|
|
1197
|
+
|
|
1198
|
+
bool is_unitless();
|
|
1199
|
+
void convert(const string& unit = "");
|
|
1200
|
+
void normalize(const string& unit = "");
|
|
1258
1201
|
// useful for making one number compatible with another
|
|
1259
|
-
string find_convertible_unit() const
|
|
1260
|
-
{
|
|
1261
|
-
for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {
|
|
1262
|
-
string u(numerator_units_[i]);
|
|
1263
|
-
if (string_to_unit(u) != INCOMMENSURABLE) return u;
|
|
1264
|
-
}
|
|
1265
|
-
for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {
|
|
1266
|
-
string u(denominator_units_[i]);
|
|
1267
|
-
if (string_to_unit(u) != INCOMMENSURABLE) return u;
|
|
1268
|
-
}
|
|
1269
|
-
return string();
|
|
1270
|
-
}
|
|
1202
|
+
string find_convertible_unit() const;
|
|
1271
1203
|
|
|
1272
|
-
virtual bool operator==(Expression& rhs) const
|
|
1273
|
-
|
|
1274
|
-
try
|
|
1275
|
-
{
|
|
1276
|
-
Number& e(dynamic_cast<Number&>(rhs));
|
|
1277
|
-
if (!e) return false;
|
|
1278
|
-
e.normalize(find_convertible_unit());
|
|
1279
|
-
return unit() == e.unit() && value() == e.value();
|
|
1280
|
-
}
|
|
1281
|
-
catch (std::bad_cast&)
|
|
1282
|
-
{
|
|
1283
|
-
return false;
|
|
1284
|
-
}
|
|
1285
|
-
catch (...) { throw; }
|
|
1286
|
-
}
|
|
1204
|
+
virtual bool operator== (Expression& rhs) const;
|
|
1205
|
+
virtual bool operator== (Expression* rhs) const;
|
|
1287
1206
|
|
|
1288
1207
|
virtual size_t hash()
|
|
1289
1208
|
{
|