sassc 1.11.4 → 1.12.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.
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
  }