sassc 1.11.4 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +2 -2
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/README.md +4 -1
  5. data/ext/libsass/.editorconfig +1 -1
  6. data/ext/libsass/.github/CONTRIBUTING.md +7 -7
  7. data/ext/libsass/.github/ISSUE_TEMPLATE.md +31 -6
  8. data/ext/libsass/.gitignore +3 -0
  9. data/ext/libsass/.travis.yml +37 -18
  10. data/ext/libsass/GNUmakefile.am +23 -37
  11. data/ext/libsass/Makefile +10 -6
  12. data/ext/libsass/Makefile.conf +3 -0
  13. data/ext/libsass/Readme.md +68 -63
  14. data/ext/libsass/appveyor.yml +7 -3
  15. data/ext/libsass/configure.ac +10 -14
  16. data/ext/libsass/docs/api-context-internal.md +29 -21
  17. data/ext/libsass/docs/api-context.md +26 -6
  18. data/ext/libsass/docs/api-doc.md +49 -16
  19. data/ext/libsass/docs/api-function-example.md +1 -1
  20. data/ext/libsass/docs/api-function.md +31 -7
  21. data/ext/libsass/docs/api-importer.md +19 -19
  22. data/ext/libsass/docs/api-value.md +4 -2
  23. data/ext/libsass/docs/build-on-windows.md +4 -4
  24. data/ext/libsass/docs/build-with-mingw.md +3 -3
  25. data/ext/libsass/docs/build.md +9 -9
  26. data/ext/libsass/docs/custom-functions-internal.md +10 -8
  27. data/ext/libsass/docs/implementations.md +20 -8
  28. data/ext/libsass/docs/unicode.md +16 -10
  29. data/ext/libsass/include/sass/base.h +0 -3
  30. data/ext/libsass/include/sass/context.h +20 -2
  31. data/ext/libsass/include/sass/functions.h +31 -0
  32. data/ext/libsass/include/sass/values.h +3 -1
  33. data/ext/libsass/include/sass/version.h +1 -1
  34. data/ext/libsass/include/sass/version.h.in +1 -1
  35. data/ext/libsass/include/sass2scss.h +1 -1
  36. data/ext/libsass/res/resource.rc +6 -6
  37. data/ext/libsass/script/ci-build-libsass +10 -5
  38. data/ext/libsass/script/ci-build-plugin +62 -0
  39. data/ext/libsass/script/ci-install-compiler +1 -1
  40. data/ext/libsass/script/ci-install-deps +4 -7
  41. data/ext/libsass/script/ci-report-coverage +13 -3
  42. data/ext/libsass/script/tap-driver +1 -1
  43. data/ext/libsass/script/tap-runner +1 -1
  44. data/ext/libsass/src/GNUmakefile.am +1 -1
  45. data/ext/libsass/src/ast.cpp +537 -762
  46. data/ext/libsass/src/ast.hpp +377 -419
  47. data/ext/libsass/src/ast_def_macros.hpp +26 -1
  48. data/ext/libsass/src/ast_fwd_decl.cpp +29 -0
  49. data/ext/libsass/src/ast_fwd_decl.hpp +94 -21
  50. data/ext/libsass/src/b64/encode.h +3 -1
  51. data/ext/libsass/src/backtrace.cpp +46 -0
  52. data/ext/libsass/src/backtrace.hpp +7 -54
  53. data/ext/libsass/src/bind.cpp +72 -50
  54. data/ext/libsass/src/bind.hpp +0 -1
  55. data/ext/libsass/src/cencode.c +6 -0
  56. data/ext/libsass/src/check_nesting.cpp +157 -135
  57. data/ext/libsass/src/check_nesting.hpp +11 -10
  58. data/ext/libsass/src/color_maps.cpp +10 -6
  59. data/ext/libsass/src/color_maps.hpp +6 -8
  60. data/ext/libsass/src/constants.cpp +4 -3
  61. data/ext/libsass/src/constants.hpp +4 -3
  62. data/ext/libsass/src/context.cpp +110 -47
  63. data/ext/libsass/src/context.hpp +11 -1
  64. data/ext/libsass/src/cssize.cpp +105 -94
  65. data/ext/libsass/src/cssize.hpp +4 -5
  66. data/ext/libsass/src/debugger.hpp +247 -244
  67. data/ext/libsass/src/emitter.cpp +30 -6
  68. data/ext/libsass/src/emitter.hpp +7 -0
  69. data/ext/libsass/src/environment.cpp +67 -16
  70. data/ext/libsass/src/environment.hpp +28 -7
  71. data/ext/libsass/src/error_handling.cpp +92 -64
  72. data/ext/libsass/src/error_handling.hpp +64 -43
  73. data/ext/libsass/src/eval.cpp +494 -544
  74. data/ext/libsass/src/eval.hpp +17 -23
  75. data/ext/libsass/src/expand.cpp +182 -154
  76. data/ext/libsass/src/expand.hpp +4 -5
  77. data/ext/libsass/src/extend.cpp +299 -291
  78. data/ext/libsass/src/extend.hpp +46 -11
  79. data/ext/libsass/src/file.cpp +103 -36
  80. data/ext/libsass/src/file.hpp +21 -4
  81. data/ext/libsass/src/functions.cpp +561 -312
  82. data/ext/libsass/src/functions.hpp +8 -5
  83. data/ext/libsass/src/inspect.cpp +108 -53
  84. data/ext/libsass/src/inspect.hpp +5 -2
  85. data/ext/libsass/src/lexer.cpp +15 -7
  86. data/ext/libsass/src/lexer.hpp +13 -4
  87. data/ext/libsass/src/listize.cpp +3 -2
  88. data/ext/libsass/src/listize.hpp +0 -1
  89. data/ext/libsass/src/memory/SharedPtr.cpp +16 -18
  90. data/ext/libsass/src/memory/SharedPtr.hpp +47 -43
  91. data/ext/libsass/src/node.cpp +34 -38
  92. data/ext/libsass/src/node.hpp +6 -8
  93. data/ext/libsass/src/operation.hpp +2 -2
  94. data/ext/libsass/src/operators.cpp +240 -0
  95. data/ext/libsass/src/operators.hpp +30 -0
  96. data/ext/libsass/src/output.cpp +22 -20
  97. data/ext/libsass/src/parser.cpp +719 -358
  98. data/ext/libsass/src/parser.hpp +57 -22
  99. data/ext/libsass/src/plugins.cpp +28 -10
  100. data/ext/libsass/src/position.cpp +21 -3
  101. data/ext/libsass/src/position.hpp +2 -1
  102. data/ext/libsass/src/prelexer.cpp +104 -19
  103. data/ext/libsass/src/prelexer.hpp +10 -3
  104. data/ext/libsass/src/remove_placeholders.cpp +9 -10
  105. data/ext/libsass/src/remove_placeholders.hpp +1 -5
  106. data/ext/libsass/src/sass.cpp +62 -4
  107. data/ext/libsass/src/sass.hpp +5 -2
  108. data/ext/libsass/src/sass_context.cpp +96 -58
  109. data/ext/libsass/src/sass_context.hpp +7 -5
  110. data/ext/libsass/src/sass_functions.cpp +63 -1
  111. data/ext/libsass/src/sass_functions.hpp +19 -1
  112. data/ext/libsass/src/sass_util.cpp +3 -3
  113. data/ext/libsass/src/sass_util.hpp +4 -4
  114. data/ext/libsass/src/sass_values.cpp +42 -39
  115. data/ext/libsass/src/sass_values.hpp +2 -1
  116. data/ext/libsass/src/source_map.cpp +16 -18
  117. data/ext/libsass/src/subset_map.cpp +6 -8
  118. data/ext/libsass/src/subset_map.hpp +6 -6
  119. data/ext/libsass/src/to_c.cpp +2 -2
  120. data/ext/libsass/src/to_value.cpp +8 -3
  121. data/ext/libsass/src/to_value.hpp +1 -0
  122. data/ext/libsass/src/units.cpp +349 -45
  123. data/ext/libsass/src/units.hpp +39 -22
  124. data/ext/libsass/src/utf8/checked.h +7 -0
  125. data/ext/libsass/src/utf8/unchecked.h +7 -0
  126. data/ext/libsass/src/utf8_string.cpp +1 -1
  127. data/ext/libsass/src/util.cpp +139 -45
  128. data/ext/libsass/src/util.hpp +4 -7
  129. data/ext/libsass/src/values.cpp +15 -23
  130. data/ext/libsass/win/libsass.sln +13 -2
  131. data/ext/libsass/win/libsass.sln.DotSettings +9 -0
  132. data/ext/libsass/win/libsass.targets +3 -0
  133. data/ext/libsass/win/libsass.vcxproj.filters +9 -0
  134. data/lib/sassc/version.rb +1 -1
  135. data/sassc.gemspec +1 -1
  136. data/test/native_test.rb +1 -1
  137. metadata +11 -4
@@ -4,22 +4,20 @@
4
4
 
5
5
  namespace Sass {
6
6
 
7
- void Subset_Map::put(const Compound_Selector_Obj& sel, const Subset_Map_Val& value)
7
+ void Subset_Map::put(const Compound_Selector_Obj& sel, const SubSetMapPair& value)
8
8
  {
9
9
  if (sel->empty()) throw std::runtime_error("internal error: subset map keys may not be empty");
10
10
  size_t index = values_.size();
11
11
  values_.push_back(value);
12
12
  for (size_t i = 0, S = sel->length(); i < S; ++i)
13
13
  {
14
- hash_[(*sel)[i]].push_back(std::make_pair(&sel, index));
14
+ hash_[(*sel)[i]].push_back(std::make_pair(sel, index));
15
15
  }
16
16
  }
17
17
 
18
- std::vector<Subset_Map_Val> Subset_Map::get_kv(const Compound_Selector_Obj& sel)
18
+ std::vector<SubSetMapPair> Subset_Map::get_kv(const Compound_Selector_Obj& sel)
19
19
  {
20
- // std::vector<Subset_Map_Key> s = sel->to_str_vec();
21
- // std::set<std::string> dict(s.begin(), s.end());
22
- std::unordered_set<Simple_Selector_Obj, HashSimpleSelector, CompareSimpleSelector> dict(sel->begin(), sel->end());
20
+ SimpleSelectorDict dict(sel->begin(), sel->end()); // XXX Set
23
21
  std::vector<size_t> indices;
24
22
  for (size_t i = 0, S = sel->length(); i < S; ++i) {
25
23
  if (!hash_.count((*sel)[i])) {
@@ -42,14 +40,14 @@ namespace Sass {
42
40
  std::vector<size_t>::iterator indices_end = unique(indices.begin(), indices.end());
43
41
  indices.resize(distance(indices.begin(), indices_end));
44
42
 
45
- std::vector<Subset_Map_Val> results;
43
+ std::vector<SubSetMapPair> results;
46
44
  for (size_t i = 0, S = indices.size(); i < S; ++i) {
47
45
  results.push_back(values_[indices[i]]);
48
46
  }
49
47
  return results;
50
48
  }
51
49
 
52
- std::vector<Subset_Map_Val> Subset_Map::get_v(const Compound_Selector_Obj& sel)
50
+ std::vector<SubSetMapPair> Subset_Map::get_v(const Compound_Selector_Obj& sel)
53
51
  {
54
52
  return get_kv(sel);
55
53
  }
@@ -60,15 +60,15 @@ namespace Sass {
60
60
 
61
61
  class Subset_Map {
62
62
  private:
63
- std::vector<Subset_Map_Val> values_;
64
- std::map<Simple_Selector_Obj, std::vector<std::pair<Compound_Selector_Obj, size_t> > > hash_;
63
+ std::vector<SubSetMapPair> values_;
64
+ std::map<Simple_Selector_Obj, std::vector<std::pair<Compound_Selector_Obj, size_t> >, OrderNodes > hash_;
65
65
  public:
66
- void put(const Compound_Selector_Obj& sel, const Subset_Map_Val& value);
67
- std::vector<Subset_Map_Val> get_kv(const Compound_Selector_Obj& s);
68
- std::vector<Subset_Map_Val> get_v(const Compound_Selector_Obj& s);
66
+ void put(const Compound_Selector_Obj& sel, const SubSetMapPair& value);
67
+ std::vector<SubSetMapPair> get_kv(const Compound_Selector_Obj& s);
68
+ std::vector<SubSetMapPair> get_v(const Compound_Selector_Obj& s);
69
69
  bool empty() { return values_.empty(); }
70
70
  void clear() { values_.clear(); hash_.clear(); }
71
- const std::vector<Subset_Map_Val> values(void) { return values_; }
71
+ const std::vector<SubSetMapPair> values(void) { return values_; }
72
72
  };
73
73
 
74
74
  }
@@ -36,7 +36,7 @@ namespace Sass {
36
36
 
37
37
  union Sass_Value* To_C::operator()(List_Ptr l)
38
38
  {
39
- union Sass_Value* v = sass_make_list(l->length(), l->separator());
39
+ union Sass_Value* v = sass_make_list(l->length(), l->separator(), l->is_bracketed());
40
40
  for (size_t i = 0, L = l->length(); i < L; ++i) {
41
41
  sass_list_set_value(v, i, (*l)[i]->perform(this));
42
42
  }
@@ -57,7 +57,7 @@ namespace Sass {
57
57
 
58
58
  union Sass_Value* To_C::operator()(Arguments_Ptr a)
59
59
  {
60
- union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA);
60
+ union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA, false);
61
61
  for (size_t i = 0, L = a->length(); i < L; ++i) {
62
62
  sass_list_set_value(v, i, (*a)[i]->perform(this));
63
63
  }
@@ -9,8 +9,6 @@ namespace Sass {
9
9
  // throw a runtime error if this happens
10
10
  // we want a well defined set of possible nodes
11
11
  throw std::runtime_error("invalid node for to_value");
12
- // mute warning
13
- return 0;
14
12
  }
15
13
 
16
14
  // Custom_Error is a valid value
@@ -62,7 +60,8 @@ namespace Sass {
62
60
  l->pstate(),
63
61
  l->length(),
64
62
  l->separator(),
65
- l->is_arglist());
63
+ l->is_arglist(),
64
+ l->is_bracketed());
66
65
  for (size_t i = 0, L = l->length(); i < L; ++i) {
67
66
  ll->append((*l)[i]->perform(this));
68
67
  }
@@ -81,6 +80,12 @@ namespace Sass {
81
80
  return n;
82
81
  }
83
82
 
83
+ // Function is a valid value
84
+ Value_Ptr To_Value::operator()(Function_Ptr n)
85
+ {
86
+ return n;
87
+ }
88
+
84
89
  // Argument returns its value
85
90
  Value_Ptr To_Value::operator()(Argument_Ptr arg)
86
91
  {
@@ -34,6 +34,7 @@ namespace Sass {
34
34
  Value_Ptr operator()(List_Ptr);
35
35
  Value_Ptr operator()(Map_Ptr);
36
36
  Value_Ptr operator()(Null_Ptr);
37
+ Value_Ptr operator()(Function_Ptr);
37
38
 
38
39
  // convert to string via `To_String`
39
40
  Value_Ptr operator()(Selector_List_Ptr);
@@ -1,6 +1,7 @@
1
1
  #include "sass.hpp"
2
2
  #include <stdexcept>
3
3
  #include "units.hpp"
4
+ #include "error_handling.hpp"
4
5
 
5
6
  namespace Sass {
6
7
 
@@ -53,12 +54,12 @@ namespace Sass {
53
54
  {
54
55
  switch (unit & 0xFF00)
55
56
  {
56
- case UnitClass::LENGTH: return UnitClass::LENGTH; break;
57
- case UnitClass::ANGLE: return UnitClass::ANGLE; break;
58
- case UnitClass::TIME: return UnitClass::TIME; break;
59
- case UnitClass::FREQUENCY: return UnitClass::FREQUENCY; break;
60
- case UnitClass::RESOLUTION: return UnitClass::RESOLUTION; break;
61
- default: return UnitClass::INCOMMENSURABLE; break;
57
+ case UnitClass::LENGTH: return UnitClass::LENGTH;
58
+ case UnitClass::ANGLE: return UnitClass::ANGLE;
59
+ case UnitClass::TIME: return UnitClass::TIME;
60
+ case UnitClass::FREQUENCY: return UnitClass::FREQUENCY;
61
+ case UnitClass::RESOLUTION: return UnitClass::RESOLUTION;
62
+ default: return UnitClass::INCOMMENSURABLE;
62
63
  }
63
64
  };
64
65
 
@@ -66,12 +67,25 @@ namespace Sass {
66
67
  {
67
68
  switch (unit & 0xFF00)
68
69
  {
69
- case UnitClass::LENGTH: return "LENGTH"; break;
70
- case UnitClass::ANGLE: return "ANGLE"; break;
71
- case UnitClass::TIME: return "TIME"; break;
72
- case UnitClass::FREQUENCY: return "FREQUENCY"; break;
73
- case UnitClass::RESOLUTION: return "RESOLUTION"; break;
74
- default: return "INCOMMENSURABLE"; break;
70
+ case UnitClass::LENGTH: return "LENGTH";
71
+ case UnitClass::ANGLE: return "ANGLE";
72
+ case UnitClass::TIME: return "TIME";
73
+ case UnitClass::FREQUENCY: return "FREQUENCY";
74
+ case UnitClass::RESOLUTION: return "RESOLUTION";
75
+ default: return "INCOMMENSURABLE";
76
+ }
77
+ };
78
+
79
+ UnitType get_main_unit(const UnitClass unit)
80
+ {
81
+ switch (unit)
82
+ {
83
+ case UnitClass::LENGTH: return UnitType::PX;
84
+ case UnitClass::ANGLE: return UnitType::DEG;
85
+ case UnitClass::TIME: return UnitType::SEC;
86
+ case UnitClass::FREQUENCY: return UnitType::HERTZ;
87
+ case UnitClass::RESOLUTION: return UnitType::DPI;
88
+ default: return UnitType::UNKNOWN;
75
89
  }
76
90
  };
77
91
 
@@ -107,29 +121,29 @@ namespace Sass {
107
121
  {
108
122
  switch (unit) {
109
123
  // size units
110
- case UnitType::PX: return "px"; break;
111
- case UnitType::PT: return "pt"; break;
112
- case UnitType::PC: return "pc"; break;
113
- case UnitType::MM: return "mm"; break;
114
- case UnitType::CM: return "cm"; break;
115
- case UnitType::IN: return "in"; break;
124
+ case UnitType::PX: return "px";
125
+ case UnitType::PT: return "pt";
126
+ case UnitType::PC: return "pc";
127
+ case UnitType::MM: return "mm";
128
+ case UnitType::CM: return "cm";
129
+ case UnitType::IN: return "in";
116
130
  // angle units
117
- case UnitType::DEG: return "deg"; break;
118
- case UnitType::GRAD: return "grad"; break;
119
- case UnitType::RAD: return "rad"; break;
120
- case UnitType::TURN: return "turn"; break;
131
+ case UnitType::DEG: return "deg";
132
+ case UnitType::GRAD: return "grad";
133
+ case UnitType::RAD: return "rad";
134
+ case UnitType::TURN: return "turn";
121
135
  // time units
122
- case UnitType::SEC: return "s"; break;
123
- case UnitType::MSEC: return "ms"; break;
136
+ case UnitType::SEC: return "s";
137
+ case UnitType::MSEC: return "ms";
124
138
  // frequency units
125
- case UnitType::HERTZ: return "Hz"; break;
126
- case UnitType::KHERTZ: return "kHz"; break;
139
+ case UnitType::HERTZ: return "Hz";
140
+ case UnitType::KHERTZ: return "kHz";
127
141
  // resolutions units
128
- case UnitType::DPI: return "dpi"; break;
129
- case UnitType::DPCM: return "dpcm"; break;
130
- case UnitType::DPPX: return "dppx"; break;
142
+ case UnitType::DPI: return "dpi";
143
+ case UnitType::DPCM: return "dpcm";
144
+ case UnitType::DPPX: return "dppx";
131
145
  // for unknown units
132
- default: return ""; break;
146
+ default: return "";
133
147
  }
134
148
  }
135
149
 
@@ -161,7 +175,7 @@ namespace Sass {
161
175
  }
162
176
 
163
177
  // throws incompatibleUnits exceptions
164
- double conversion_factor(const std::string& s1, const std::string& s2, bool strict)
178
+ double conversion_factor(const std::string& s1, const std::string& s2)
165
179
  {
166
180
  // assert for same units
167
181
  if (s1 == s2) return 1;
@@ -171,27 +185,317 @@ namespace Sass {
171
185
  // query unit group types
172
186
  UnitClass t1 = get_unit_type(u1);
173
187
  UnitClass t2 = get_unit_type(u2);
188
+ // return the conversion factor
189
+ return conversion_factor(u1, u2, t1, t2);
190
+ }
191
+
192
+ // throws incompatibleUnits exceptions
193
+ double conversion_factor(UnitType u1, UnitType u2, UnitClass t1, UnitClass t2)
194
+ {
195
+ // can't convert between groups
196
+ if (t1 != t2) return 0;
174
197
  // get absolute offset
175
198
  // used for array acces
176
199
  size_t i1 = u1 - t1;
177
200
  size_t i2 = u2 - t2;
178
- // error if units are not of the same group
179
- // don't error for multiplication and division
180
- if (strict && t1 != t2) throw incompatibleUnits(u1, u2);
181
- // only process known units
182
- if (u1 != UNKNOWN && u2 != UNKNOWN) {
183
- switch (t1) {
184
- case UnitClass::LENGTH: return size_conversion_factors[i1][i2]; break;
185
- case UnitClass::ANGLE: return angle_conversion_factors[i1][i2]; break;
186
- case UnitClass::TIME: return time_conversion_factors[i1][i2]; break;
187
- case UnitClass::FREQUENCY: return frequency_conversion_factors[i1][i2]; break;
188
- case UnitClass::RESOLUTION: return resolution_conversion_factors[i1][i2]; break;
189
- // ToDo: should we throw error here?
190
- case UnitClass::INCOMMENSURABLE: return 0; break;
191
- }
201
+ // process known units
202
+ switch (t1) {
203
+ case LENGTH:
204
+ return size_conversion_factors[i1][i2];
205
+ case ANGLE:
206
+ return angle_conversion_factors[i1][i2];
207
+ case TIME:
208
+ return time_conversion_factors[i1][i2];
209
+ case FREQUENCY:
210
+ return frequency_conversion_factors[i1][i2];
211
+ case RESOLUTION:
212
+ return resolution_conversion_factors[i1][i2];
213
+ case INCOMMENSURABLE:
214
+ return 0;
192
215
  }
193
216
  // fallback
194
217
  return 0;
195
218
  }
196
219
 
220
+ double convert_units(const std::string& lhs, const std::string& rhs, int& lhsexp, int& rhsexp)
221
+ {
222
+ double f = 0;
223
+ // do not convert same ones
224
+ if (lhs == rhs) return 0;
225
+ // skip already canceled out unit
226
+ if (lhsexp == 0) return 0;
227
+ if (rhsexp == 0) return 0;
228
+ // check if it can be converted
229
+ UnitType ulhs = string_to_unit(lhs);
230
+ UnitType urhs = string_to_unit(rhs);
231
+ // skip units we cannot convert
232
+ if (ulhs == UNKNOWN) return 0;
233
+ if (urhs == UNKNOWN) return 0;
234
+ // query unit group types
235
+ UnitClass clhs = get_unit_type(ulhs);
236
+ UnitClass crhs = get_unit_type(urhs);
237
+ // skip units we cannot convert
238
+ if (clhs != crhs) return 0;
239
+ // if right denominator is bigger than lhs, we want to keep it in rhs unit
240
+ if (rhsexp < 0 && lhsexp > 0 && - rhsexp > lhsexp) {
241
+ // get the conversion factor for units
242
+ f = conversion_factor(urhs, ulhs, clhs, crhs);
243
+ // left hand side has been consumned
244
+ f = std::pow(f, lhsexp);
245
+ rhsexp += lhsexp;
246
+ lhsexp = 0;
247
+ }
248
+ else {
249
+ // get the conversion factor for units
250
+ f = conversion_factor(ulhs, urhs, clhs, crhs);
251
+ // right hand side has been consumned
252
+ f = std::pow(f, rhsexp);
253
+ lhsexp += rhsexp;
254
+ rhsexp = 0;
255
+ }
256
+ return f;
257
+ }
258
+
259
+ bool Units::operator< (const Units& rhs) const
260
+ {
261
+ return (numerators < rhs.numerators) &&
262
+ (denominators < rhs.denominators);
263
+ }
264
+ bool Units::operator== (const Units& rhs) const
265
+ {
266
+ return (numerators == rhs.numerators) &&
267
+ (denominators == rhs.denominators);
268
+ }
269
+
270
+ double Units::normalize()
271
+ {
272
+
273
+ size_t iL = numerators.size();
274
+ size_t nL = denominators.size();
275
+
276
+ // the final conversion factor
277
+ double factor = 1;
278
+
279
+ for (size_t i = 0; i < iL; i++) {
280
+ std::string &lhs = numerators[i];
281
+ UnitType ulhs = string_to_unit(lhs);
282
+ if (ulhs == UNKNOWN) continue;
283
+ UnitClass clhs = get_unit_type(ulhs);
284
+ UnitType umain = get_main_unit(clhs);
285
+ if (ulhs == umain) continue;
286
+ double f(conversion_factor(umain, ulhs, clhs, clhs));
287
+ if (f == 0) throw std::runtime_error("INVALID");
288
+ numerators[i] = unit_to_string(umain);
289
+ factor /= f;
290
+ }
291
+
292
+ for (size_t n = 0; n < nL; n++) {
293
+ std::string &rhs = denominators[n];
294
+ UnitType urhs = string_to_unit(rhs);
295
+ if (urhs == UNKNOWN) continue;
296
+ UnitClass crhs = get_unit_type(urhs);
297
+ UnitType umain = get_main_unit(crhs);
298
+ if (urhs == umain) continue;
299
+ double f(conversion_factor(umain, urhs, crhs, crhs));
300
+ if (f == 0) throw std::runtime_error("INVALID");
301
+ denominators[n] = unit_to_string(umain);
302
+ factor /= f;
303
+ }
304
+
305
+ std::sort (numerators.begin(), numerators.end());
306
+ std::sort (denominators.begin(), denominators.end());
307
+
308
+ // return for conversion
309
+ return factor;
310
+ }
311
+
312
+ double Units::reduce()
313
+ {
314
+
315
+ size_t iL = numerators.size();
316
+ size_t nL = denominators.size();
317
+
318
+ // have less than two units?
319
+ if (iL + nL < 2) return 1;
320
+
321
+ // first make sure same units cancel each other out
322
+ // it seems that a map table will fit nicely to do this
323
+ // we basically construct exponents for each unit
324
+ // has the advantage that they will be pre-sorted
325
+ std::map<std::string, int> exponents;
326
+
327
+ // initialize by summing up occurences in unit vectors
328
+ // this will already cancel out equivalent units (e.q. px/px)
329
+ for (size_t i = 0; i < iL; i ++) exponents[numerators[i]] += 1;
330
+ for (size_t n = 0; n < nL; n ++) exponents[denominators[n]] -= 1;
331
+
332
+ // the final conversion factor
333
+ double factor = 1;
334
+
335
+ // convert between compatible units
336
+ for (size_t i = 0; i < iL; i++) {
337
+ for (size_t n = 0; n < nL; n++) {
338
+ std::string &lhs = numerators[i], &rhs = denominators[n];
339
+ int &lhsexp = exponents[lhs], &rhsexp = exponents[rhs];
340
+ double f(convert_units(lhs, rhs, lhsexp, rhsexp));
341
+ if (f == 0) continue;
342
+ factor /= f;
343
+ }
344
+ }
345
+
346
+ // now we can build up the new unit arrays
347
+ numerators.clear();
348
+ denominators.clear();
349
+
350
+ // recreate sorted units vectors
351
+ for (auto exp : exponents) {
352
+ int &exponent = exp.second;
353
+ while (exponent > 0 && exponent --)
354
+ numerators.push_back(exp.first);
355
+ while (exponent < 0 && exponent ++)
356
+ denominators.push_back(exp.first);
357
+ }
358
+
359
+ // return for conversion
360
+ return factor;
361
+
362
+ }
363
+
364
+ std::string Units::unit() const
365
+ {
366
+ std::string u;
367
+ size_t iL = numerators.size();
368
+ size_t nL = denominators.size();
369
+ for (size_t i = 0; i < iL; i += 1) {
370
+ if (i) u += '*';
371
+ u += numerators[i];
372
+ }
373
+ if (nL != 0) u += '/';
374
+ for (size_t n = 0; n < nL; n += 1) {
375
+ if (n) u += '*';
376
+ u += denominators[n];
377
+ }
378
+ return u;
379
+ }
380
+
381
+ bool Units::is_unitless() const
382
+ {
383
+ return numerators.empty() &&
384
+ denominators.empty();
385
+ }
386
+
387
+ bool Units::is_valid_css_unit() const
388
+ {
389
+ return numerators.size() <= 1 &&
390
+ denominators.size() == 0;
391
+ }
392
+
393
+ // this does not cover all cases (multiple prefered units)
394
+ double Units::convert_factor(const Units& r) const
395
+ {
396
+
397
+ std::vector<std::string> miss_nums(0);
398
+ std::vector<std::string> miss_dens(0);
399
+ // create copy since we need these for state keeping
400
+ std::vector<std::string> r_nums(r.numerators);
401
+ std::vector<std::string> r_dens(r.denominators);
402
+
403
+ auto l_num_it = numerators.begin();
404
+ auto l_num_end = numerators.end();
405
+
406
+ bool l_unitless = is_unitless();
407
+ auto r_unitless = r.is_unitless();
408
+
409
+ // overall conversion
410
+ double factor = 1;
411
+
412
+ // process all left numerators
413
+ while (l_num_it != l_num_end)
414
+ {
415
+ // get and increment afterwards
416
+ const std::string l_num = *(l_num_it ++);
417
+
418
+ auto r_num_it = r_nums.begin(), r_num_end = r_nums.end();
419
+
420
+ bool found = false;
421
+ // search for compatible numerator
422
+ while (r_num_it != r_num_end)
423
+ {
424
+ // get and increment afterwards
425
+ const std::string r_num = *(r_num_it);
426
+ // get possible conversion factor for units
427
+ double conversion = conversion_factor(l_num, r_num);
428
+ // skip incompatible numerator
429
+ if (conversion == 0) {
430
+ ++ r_num_it;
431
+ continue;
432
+ }
433
+ // apply to global factor
434
+ factor *= conversion;
435
+ // remove item from vector
436
+ r_nums.erase(r_num_it);
437
+ // found numerator
438
+ found = true;
439
+ break;
440
+ }
441
+ // maybe we did not find any
442
+ // left numerator is leftover
443
+ if (!found) miss_nums.push_back(l_num);
444
+ }
445
+
446
+ auto l_den_it = denominators.begin();
447
+ auto l_den_end = denominators.end();
448
+
449
+ // process all left denominators
450
+ while (l_den_it != l_den_end)
451
+ {
452
+ // get and increment afterwards
453
+ const std::string l_den = *(l_den_it ++);
454
+
455
+ auto r_den_it = r_dens.begin();
456
+ auto r_den_end = r_dens.end();
457
+
458
+ bool found = false;
459
+ // search for compatible denominator
460
+ while (r_den_it != r_den_end)
461
+ {
462
+ // get and increment afterwards
463
+ const std::string r_den = *(r_den_it);
464
+ // get possible converstion factor for units
465
+ double conversion = conversion_factor(l_den, r_den);
466
+ // skip incompatible denominator
467
+ if (conversion == 0) {
468
+ ++ r_den_it;
469
+ continue;
470
+ }
471
+ // apply to global factor
472
+ factor /= conversion;
473
+ // remove item from vector
474
+ r_dens.erase(r_den_it);
475
+ // found denominator
476
+ found = true;
477
+ break;
478
+ }
479
+ // maybe we did not find any
480
+ // left denominator is leftover
481
+ if (!found) miss_dens.push_back(l_den);
482
+ }
483
+
484
+ // check left-overs (ToDo: might cancel out?)
485
+ if (miss_nums.size() > 0 && !r_unitless) {
486
+ throw Exception::IncompatibleUnits(r, *this);
487
+ }
488
+ else if (miss_dens.size() > 0 && !r_unitless) {
489
+ throw Exception::IncompatibleUnits(r, *this);
490
+ }
491
+ else if (r_nums.size() > 0 && !l_unitless) {
492
+ throw Exception::IncompatibleUnits(r, *this);
493
+ }
494
+ else if (r_dens.size() > 0 && !l_unitless) {
495
+ throw Exception::IncompatibleUnits(r, *this);
496
+ }
497
+
498
+ return factor;
499
+ }
500
+
197
501
  }