sassc 1.8.3 → 1.8.4

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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/ext/libsass/.editorconfig +1 -1
  4. data/ext/libsass/.gitignore +1 -0
  5. data/ext/libsass/LICENSE +1 -1
  6. data/ext/libsass/Makefile +20 -14
  7. data/ext/libsass/Makefile.conf +0 -1
  8. data/ext/libsass/Readme.md +3 -1
  9. data/ext/libsass/appveyor.yml +19 -11
  10. data/ext/libsass/docs/api-importer-example.md +2 -1235
  11. data/ext/libsass/docs/build-with-autotools.md +10 -0
  12. data/ext/libsass/docs/build-with-makefiles.md +18 -0
  13. data/ext/libsass/include/sass/base.h +4 -1
  14. data/ext/libsass/include/sass/values.h +2 -1
  15. data/ext/libsass/src/ast.cpp +279 -346
  16. data/ext/libsass/src/ast.hpp +234 -60
  17. data/ext/libsass/src/base64vlq.cpp +1 -0
  18. data/ext/libsass/src/bind.cpp +35 -45
  19. data/ext/libsass/src/bind.hpp +1 -0
  20. data/ext/libsass/src/color_maps.cpp +1 -0
  21. data/ext/libsass/src/constants.cpp +4 -1
  22. data/ext/libsass/src/constants.hpp +2 -1
  23. data/ext/libsass/src/context.cpp +41 -31
  24. data/ext/libsass/src/context.hpp +10 -10
  25. data/ext/libsass/src/cssize.cpp +7 -4
  26. data/ext/libsass/src/cssize.hpp +1 -3
  27. data/ext/libsass/src/debugger.hpp +73 -14
  28. data/ext/libsass/src/emitter.cpp +37 -25
  29. data/ext/libsass/src/emitter.hpp +10 -9
  30. data/ext/libsass/src/environment.cpp +16 -5
  31. data/ext/libsass/src/environment.hpp +5 -3
  32. data/ext/libsass/src/error_handling.cpp +91 -14
  33. data/ext/libsass/src/error_handling.hpp +105 -4
  34. data/ext/libsass/src/eval.cpp +519 -330
  35. data/ext/libsass/src/eval.hpp +12 -13
  36. data/ext/libsass/src/expand.cpp +92 -56
  37. data/ext/libsass/src/expand.hpp +5 -3
  38. data/ext/libsass/src/extend.cpp +60 -51
  39. data/ext/libsass/src/extend.hpp +1 -3
  40. data/ext/libsass/src/file.cpp +37 -27
  41. data/ext/libsass/src/functions.cpp +78 -62
  42. data/ext/libsass/src/functions.hpp +1 -0
  43. data/ext/libsass/src/inspect.cpp +293 -64
  44. data/ext/libsass/src/inspect.hpp +2 -0
  45. data/ext/libsass/src/lexer.cpp +1 -0
  46. data/ext/libsass/src/listize.cpp +14 -15
  47. data/ext/libsass/src/listize.hpp +3 -5
  48. data/ext/libsass/src/memory_manager.cpp +1 -0
  49. data/ext/libsass/src/node.cpp +2 -3
  50. data/ext/libsass/src/operation.hpp +70 -71
  51. data/ext/libsass/src/output.cpp +28 -32
  52. data/ext/libsass/src/output.hpp +1 -2
  53. data/ext/libsass/src/parser.cpp +402 -183
  54. data/ext/libsass/src/parser.hpp +19 -9
  55. data/ext/libsass/src/plugins.cpp +1 -0
  56. data/ext/libsass/src/position.cpp +1 -0
  57. data/ext/libsass/src/prelexer.cpp +134 -56
  58. data/ext/libsass/src/prelexer.hpp +51 -3
  59. data/ext/libsass/src/remove_placeholders.cpp +35 -9
  60. data/ext/libsass/src/remove_placeholders.hpp +4 -3
  61. data/ext/libsass/src/sass.cpp +1 -0
  62. data/ext/libsass/src/sass.hpp +129 -0
  63. data/ext/libsass/src/sass_context.cpp +31 -14
  64. data/ext/libsass/src/sass_context.hpp +2 -31
  65. data/ext/libsass/src/sass_functions.cpp +1 -0
  66. data/ext/libsass/src/sass_interface.cpp +5 -6
  67. data/ext/libsass/src/sass_util.cpp +1 -2
  68. data/ext/libsass/src/sass_util.hpp +5 -5
  69. data/ext/libsass/src/sass_values.cpp +13 -10
  70. data/ext/libsass/src/source_map.cpp +4 -3
  71. data/ext/libsass/src/source_map.hpp +2 -2
  72. data/ext/libsass/src/subset_map.hpp +0 -1
  73. data/ext/libsass/src/to_c.cpp +1 -0
  74. data/ext/libsass/src/to_c.hpp +1 -3
  75. data/ext/libsass/src/to_value.cpp +3 -5
  76. data/ext/libsass/src/to_value.hpp +1 -1
  77. data/ext/libsass/src/units.cpp +96 -59
  78. data/ext/libsass/src/units.hpp +10 -8
  79. data/ext/libsass/src/utf8_string.cpp +5 -0
  80. data/ext/libsass/src/util.cpp +23 -156
  81. data/ext/libsass/src/util.hpp +10 -14
  82. data/ext/libsass/src/values.cpp +1 -0
  83. data/ext/libsass/test/test_node.cpp +2 -6
  84. data/ext/libsass/test/test_selector_difference.cpp +1 -3
  85. data/ext/libsass/test/test_specificity.cpp +0 -2
  86. data/ext/libsass/test/test_superselector.cpp +0 -2
  87. data/ext/libsass/test/test_unification.cpp +1 -3
  88. data/ext/libsass/win/libsass.targets +18 -5
  89. data/ext/libsass/win/libsass.vcxproj +9 -7
  90. data/ext/libsass/win/libsass.vcxproj.filters +148 -106
  91. data/lib/sassc/version.rb +1 -1
  92. data/test/engine_test.rb +12 -0
  93. data/test/native_test.rb +1 -1
  94. metadata +3 -4
  95. data/ext/libsass/src/to_string.cpp +0 -48
  96. data/ext/libsass/src/to_string.hpp +0 -38
@@ -7,6 +7,16 @@ git clone https://github.com/sass/sassc.git libsass/sassc
7
7
  git clone https://github.com/sass/sass-spec.git libsass/sass-spec
8
8
  ```
9
9
 
10
+ ### Prerequisites
11
+
12
+ In order to run autotools you need a few tools installed on your system.
13
+ ```bash
14
+ yum install automake libtool # RedHat Linux
15
+ emerge -a automake libtool # Gentoo Linux
16
+ pkgin install automake libtool # SmartOS
17
+ ```
18
+
19
+
10
20
  ### Create configure script
11
21
  ```bash
12
22
  cd libsass
@@ -32,6 +32,24 @@ $ ls libsass/lib
32
32
  libsass.a libsass.so
33
33
  ```
34
34
 
35
+ ### Install onto the system
36
+
37
+ We recommend to use [autotools to install](build-with-autotools.md) libsass onto the
38
+ system, since that brings all the benefits of using libtools as the main install method.
39
+ If you still want to install libsass via the makefile, you need to make sure that gnu
40
+ `install` utility (or compatible) is installed on your system.
41
+ ```bash
42
+ yum install coreutils # RedHat Linux
43
+ emerge -a coreutils # Gentoo Linux
44
+ pkgin install coreutils # SmartOS
45
+ ```
46
+
47
+ You can set the install location by setting `PREFIX`
48
+ ```bash
49
+ PREFIX="/opt/local" make install
50
+ ```
51
+
52
+
35
53
  ### Compling sassc
36
54
 
37
55
  ```bash
@@ -56,7 +56,10 @@ enum Sass_Output_Style {
56
56
  SASS_STYLE_NESTED,
57
57
  SASS_STYLE_EXPANDED,
58
58
  SASS_STYLE_COMPACT,
59
- SASS_STYLE_COMPRESSED
59
+ SASS_STYLE_COMPRESSED,
60
+ // only used internaly
61
+ SASS_STYLE_INSPECT,
62
+ SASS_STYLE_TO_SASS
60
63
  };
61
64
 
62
65
  // Some convenient string helper function
@@ -29,7 +29,8 @@ enum Sass_Tag {
29
29
  // Tags for denoting Sass list separators
30
30
  enum Sass_Separator {
31
31
  SASS_COMMA,
32
- SASS_SPACE
32
+ SASS_SPACE,
33
+ SASS_HASH
33
34
  };
34
35
 
35
36
  // Value Operators
@@ -1,8 +1,9 @@
1
+ #include "sass.hpp"
1
2
  #include "ast.hpp"
2
3
  #include "context.hpp"
3
4
  #include "node.hpp"
4
5
  #include "extend.hpp"
5
- #include "to_string.hpp"
6
+ #include "emitter.hpp"
6
7
  #include "color_maps.hpp"
7
8
  #include <set>
8
9
  #include <iomanip>
@@ -58,7 +59,10 @@ namespace Sass {
58
59
 
59
60
  bool Compound_Selector::has_parent_ref()
60
61
  {
61
- return has_parent_reference();
62
+ for (Simple_Selector* s : *this) {
63
+ if (s->has_parent_ref()) return true;
64
+ }
65
+ return false;
62
66
  }
63
67
 
64
68
  bool Complex_Selector::has_parent_ref()
@@ -205,10 +209,8 @@ namespace Sass {
205
209
 
206
210
  bool Simple_Selector::operator== (const Simple_Selector& rhs) const
207
211
  {
208
- const Attribute_Selector* ll = dynamic_cast<const Attribute_Selector*>(this);
209
- const Attribute_Selector* rr = dynamic_cast<const Attribute_Selector*>(&rhs);
210
- if (ll && rr) return *ll == *rr;
211
-
212
+ if (const Wrapped_Selector* lw = dynamic_cast<const Wrapped_Selector*>(this)) return *lw == rhs;
213
+ if (const Attribute_Selector* la = dynamic_cast<const Attribute_Selector*>(this)) return *la == rhs;
212
214
  if (is_ns_eq(ns(), rhs.ns()))
213
215
  { return name() == rhs.name(); }
214
216
  return ns() == rhs.ns();
@@ -216,10 +218,8 @@ namespace Sass {
216
218
 
217
219
  bool Simple_Selector::operator< (const Simple_Selector& rhs) const
218
220
  {
219
- const Attribute_Selector* ll = dynamic_cast<const Attribute_Selector*>(this);
220
- const Attribute_Selector* rr = dynamic_cast<const Attribute_Selector*>(&rhs);
221
- if (ll && rr) return *ll < *rr;
222
-
221
+ if (const Wrapped_Selector* lw = dynamic_cast<const Wrapped_Selector*>(this)) return *lw < rhs;
222
+ if (const Attribute_Selector* la = dynamic_cast<const Attribute_Selector*>(this)) return *la < rhs;
223
223
  if (is_ns_eq(ns(), rhs.ns()))
224
224
  { return name() < rhs.name(); }
225
225
  return ns() < rhs.ns();
@@ -280,9 +280,8 @@ namespace Sass {
280
280
 
281
281
  Compound_Selector* Simple_Selector::unify_with(Compound_Selector* rhs, Context& ctx)
282
282
  {
283
- To_String to_string(&ctx);
284
283
  for (size_t i = 0, L = rhs->length(); i < L; ++i)
285
- { if (perform(&to_string) == (*rhs)[i]->perform(&to_string)) return rhs; }
284
+ { if (to_string(ctx.c_options) == (*rhs)[i]->to_string(ctx.c_options)) return rhs; }
286
285
 
287
286
  // check for pseudo elements because they are always last
288
287
  size_t i, L;
@@ -291,7 +290,7 @@ namespace Sass {
291
290
  {
292
291
  for (i = 0, L = rhs->length(); i < L; ++i)
293
292
  {
294
- if ((typeid(*(*rhs)[i]) == typeid(Pseudo_Selector) || typeid(*(*rhs)[i]) == typeid(Wrapped_Selector)) && (*rhs)[L-1]->is_pseudo_element())
293
+ if ((dynamic_cast<Pseudo_Selector*>((*rhs)[i]) || dynamic_cast<Wrapped_Selector*>((*rhs)[i])) && (*rhs)[L-1]->is_pseudo_element())
295
294
  { found = true; break; }
296
295
  }
297
296
  }
@@ -299,7 +298,7 @@ namespace Sass {
299
298
  {
300
299
  for (i = 0, L = rhs->length(); i < L; ++i)
301
300
  {
302
- if (typeid(*(*rhs)[i]) == typeid(Pseudo_Selector) || typeid(*(*rhs)[i]) == typeid(Wrapped_Selector))
301
+ if (dynamic_cast<Pseudo_Selector*>((*rhs)[i]) || dynamic_cast<Wrapped_Selector*>((*rhs)[i]))
303
302
  { found = true; break; }
304
303
  }
305
304
  }
@@ -500,6 +499,26 @@ namespace Sass {
500
499
  return ns() == rhs.ns();
501
500
  }
502
501
 
502
+ bool Wrapped_Selector::operator< (const Wrapped_Selector& rhs) const
503
+ {
504
+ if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name())
505
+ { return *(selector()) < *(rhs.selector()); }
506
+ if (is_ns_eq(ns(), rhs.ns()))
507
+ { return name() < rhs.name(); }
508
+ return ns() < rhs.ns();
509
+ }
510
+
511
+ bool Wrapped_Selector::operator< (const Simple_Selector& rhs) const
512
+ {
513
+ if (const Wrapped_Selector* w = dynamic_cast<const Wrapped_Selector*>(&rhs))
514
+ {
515
+ return *this < *w;
516
+ }
517
+ if (is_ns_eq(ns(), rhs.ns()))
518
+ { return name() < rhs.name(); }
519
+ return ns() < rhs.ns();
520
+ }
521
+
503
522
  bool Wrapped_Selector::is_superselector_of(Wrapped_Selector* sub)
504
523
  {
505
524
  if (this->name() != sub->name()) return false;
@@ -531,8 +550,6 @@ namespace Sass {
531
550
 
532
551
  bool Compound_Selector::is_superselector_of(Compound_Selector* rhs, std::string wrapping)
533
552
  {
534
- To_String to_string;
535
-
536
553
  Compound_Selector* lhs = this;
537
554
  Simple_Selector* lbase = lhs->base();
538
555
  Simple_Selector* rbase = rhs->base();
@@ -543,7 +560,7 @@ namespace Sass {
543
560
  for (size_t i = 0, L = length(); i < L; ++i)
544
561
  {
545
562
  if ((*this)[i]->is_pseudo_element()) {
546
- std::string pseudo((*this)[i]->perform(&to_string));
563
+ std::string pseudo((*this)[i]->to_string());
547
564
  pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving
548
565
  lpsuedoset.insert(pseudo);
549
566
  }
@@ -551,7 +568,7 @@ namespace Sass {
551
568
  for (size_t i = 0, L = rhs->length(); i < L; ++i)
552
569
  {
553
570
  if ((*rhs)[i]->is_pseudo_element()) {
554
- std::string pseudo((*rhs)[i]->perform(&to_string));
571
+ std::string pseudo((*rhs)[i]->to_string());
555
572
  pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving
556
573
  rpsuedoset.insert(pseudo);
557
574
  }
@@ -564,11 +581,11 @@ namespace Sass {
564
581
 
565
582
  if (lbase && rbase)
566
583
  {
567
- if (lbase->perform(&to_string) == rbase->perform(&to_string)) {
584
+ if (lbase->to_string() == rbase->to_string()) {
568
585
  for (size_t i = 1, L = length(); i < L; ++i)
569
- { lset.insert((*this)[i]->perform(&to_string)); }
586
+ { lset.insert((*this)[i]->to_string()); }
570
587
  for (size_t i = 1, L = rhs->length(); i < L; ++i)
571
- { rset.insert((*rhs)[i]->perform(&to_string)); }
588
+ { rset.insert((*rhs)[i]->to_string()); }
572
589
  return includes(rset.begin(), rset.end(), lset.begin(), lset.end());
573
590
  }
574
591
  return false;
@@ -602,13 +619,13 @@ namespace Sass {
602
619
  if (wrapped->name() == wrapped_r->name()) {
603
620
  if (wrapped->is_superselector_of(wrapped_r)) {
604
621
  continue;
605
- rset.insert(lhs->perform(&to_string));
622
+ rset.insert(lhs->to_string());
606
623
 
607
624
  }}
608
625
  }
609
626
  }
610
627
  // match from here on as strings
611
- lset.insert(lhs->perform(&to_string));
628
+ lset.insert(lhs->to_string());
612
629
  }
613
630
 
614
631
  for (size_t n = 0, nL = rhs->length(); n < nL; ++n)
@@ -631,7 +648,7 @@ namespace Sass {
631
648
  }
632
649
  }
633
650
  }
634
- rset.insert(r->perform(&to_string));
651
+ rset.insert(r->to_string());
635
652
  }
636
653
 
637
654
  //for (auto l : lset) { cerr << "l: " << l << endl; }
@@ -767,7 +784,6 @@ namespace Sass {
767
784
  bool Complex_Selector::is_superselector_of(Complex_Selector* rhs, std::string wrapping)
768
785
  {
769
786
  Complex_Selector* lhs = this;
770
- To_String to_string;
771
787
  // check for selectors with leading or trailing combinators
772
788
  if (!lhs->head() || !rhs->head())
773
789
  { return false; }
@@ -793,7 +809,7 @@ namespace Sass {
793
809
  if (lhs_tail->combinator() != rhs_tail->combinator()) return false;
794
810
  if (lhs_tail->head() && !rhs_tail->head()) return false;
795
811
  if (!lhs_tail->head() && rhs_tail->head()) return false;
796
- if (lhs_tail->head() && lhs_tail->head()) {
812
+ if (lhs_tail->head() && rhs_tail->head()) {
797
813
  if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false;
798
814
  }
799
815
  }
@@ -955,11 +971,12 @@ namespace Sass {
955
971
 
956
972
  if (head && head->length() > 0) {
957
973
 
974
+ Selector_List* retval = 0;
958
975
  // we have a parent selector in a simple compound list
959
976
  // mix parent complex selector into the compound list
960
977
  if (dynamic_cast<Parent_Selector*>((*head)[0])) {
978
+ retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());
961
979
  if (parents && parents->length()) {
962
- Selector_List* retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());
963
980
  if (tails && tails->length() > 0) {
964
981
  for (size_t n = 0, nL = tails->length(); n < nL; ++n) {
965
982
  for (size_t i = 0, iL = parents->length(); i < iL; ++i) {
@@ -998,11 +1015,9 @@ namespace Sass {
998
1015
  *retval << s;
999
1016
  }
1000
1017
  }
1001
- return retval;
1002
1018
  }
1003
1019
  // have no parent but some tails
1004
1020
  else {
1005
- Selector_List* retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());
1006
1021
  if (tails && tails->length() > 0) {
1007
1022
  for (size_t n = 0, nL = tails->length(); n < nL; ++n) {
1008
1023
  Complex_Selector* cpy = this->clone(ctx);
@@ -1023,14 +1038,23 @@ namespace Sass {
1023
1038
  if (!cpy->head()->length()) cpy->head(0);
1024
1039
  *retval << cpy->skip_empty_reference();
1025
1040
  }
1026
- return retval;
1027
1041
  }
1028
1042
  }
1029
1043
  // no parent selector in head
1030
1044
  else {
1031
- return this->tails(ctx, tails);
1045
+ retval = this->tails(ctx, tails);
1032
1046
  }
1033
1047
 
1048
+ for (Simple_Selector* ss : *head) {
1049
+ if (Wrapped_Selector* ws = dynamic_cast<Wrapped_Selector*>(ss)) {
1050
+ if (Selector_List* sl = dynamic_cast<Selector_List*>(ws->selector())) {
1051
+ if (parents) ws->selector(sl->parentize(parents, ctx));
1052
+ }
1053
+ }
1054
+ }
1055
+
1056
+ return retval;
1057
+
1034
1058
  }
1035
1059
  // has no head
1036
1060
  else {
@@ -1141,6 +1165,7 @@ namespace Sass {
1141
1165
  Complex_Selector* Complex_Selector::clone(Context& ctx) const
1142
1166
  {
1143
1167
  Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this);
1168
+ cpy->is_optional(this->is_optional());
1144
1169
  cpy->media_block(this->media_block());
1145
1170
  if (tail()) cpy->tail(tail()->clone(ctx));
1146
1171
  return cpy;
@@ -1149,7 +1174,8 @@ namespace Sass {
1149
1174
  Complex_Selector* Complex_Selector::cloneFully(Context& ctx) const
1150
1175
  {
1151
1176
  Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this);
1152
-
1177
+ cpy->is_optional(this->is_optional());
1178
+ cpy->media_block(this->media_block());
1153
1179
  if (head()) {
1154
1180
  cpy->head(head()->clone(ctx));
1155
1181
  }
@@ -1164,13 +1190,16 @@ namespace Sass {
1164
1190
  Compound_Selector* Compound_Selector::clone(Context& ctx) const
1165
1191
  {
1166
1192
  Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *this);
1193
+ cpy->is_optional(this->is_optional());
1167
1194
  cpy->media_block(this->media_block());
1195
+ cpy->extended(this->extended());
1168
1196
  return cpy;
1169
1197
  }
1170
1198
 
1171
1199
  Selector_List* Selector_List::clone(Context& ctx) const
1172
1200
  {
1173
1201
  Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, *this);
1202
+ cpy->is_optional(this->is_optional());
1174
1203
  cpy->media_block(this->media_block());
1175
1204
  return cpy;
1176
1205
  }
@@ -1178,6 +1207,8 @@ namespace Sass {
1178
1207
  Selector_List* Selector_List::cloneFully(Context& ctx) const
1179
1208
  {
1180
1209
  Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());
1210
+ cpy->is_optional(this->is_optional());
1211
+ cpy->media_block(this->media_block());
1181
1212
  for (size_t i = 0, L = length(); i < L; ++i) {
1182
1213
  *cpy << (*this)[i]->cloneFully(ctx);
1183
1214
  }
@@ -1213,9 +1244,17 @@ namespace Sass {
1213
1244
  }
1214
1245
  }
1215
1246
 
1247
+ bool Selector_List::has_parent_ref()
1248
+ {
1249
+ for (Complex_Selector* s : *this) {
1250
+ if (s->has_parent_ref()) return true;
1251
+ }
1252
+ return false;
1253
+ }
1254
+
1216
1255
  void Selector_List::adjust_after_pushing(Complex_Selector* c)
1217
1256
  {
1218
- if (c->has_reference()) has_reference(true);
1257
+ // if (c->has_reference()) has_reference(true);
1219
1258
  }
1220
1259
 
1221
1260
  // it's a superselector if every selector of the right side
@@ -1287,8 +1326,8 @@ namespace Sass {
1287
1326
  return final_result;
1288
1327
  }
1289
1328
 
1290
- void Selector_List::populate_extends(Selector_List* extendee, Context& ctx, ExtensionSubsetMap& extends) {
1291
- To_String to_string;
1329
+ void Selector_List::populate_extends(Selector_List* extendee, Context& ctx, ExtensionSubsetMap& extends)
1330
+ {
1292
1331
 
1293
1332
  Selector_List* extender = this;
1294
1333
  for (auto complex_sel : extendee->elements()) {
@@ -1322,17 +1361,15 @@ namespace Sass {
1322
1361
 
1323
1362
  std::vector<std::string> Compound_Selector::to_str_vec()
1324
1363
  {
1325
- To_String to_string;
1326
1364
  std::vector<std::string> result;
1327
1365
  result.reserve(length());
1328
1366
  for (size_t i = 0, L = length(); i < L; ++i)
1329
- { result.push_back((*this)[i]->perform(&to_string)); }
1367
+ { result.push_back((*this)[i]->to_string()); }
1330
1368
  return result;
1331
1369
  }
1332
1370
 
1333
1371
  Compound_Selector* Compound_Selector::minus(Compound_Selector* rhs, Context& ctx)
1334
1372
  {
1335
- To_String to_string(&ctx);
1336
1373
  Compound_Selector* result = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate());
1337
1374
  // result->has_parent_reference(has_parent_reference());
1338
1375
 
@@ -1340,10 +1377,10 @@ namespace Sass {
1340
1377
  for (size_t i = 0, L = length(); i < L; ++i)
1341
1378
  {
1342
1379
  bool found = false;
1343
- std::string thisSelector((*this)[i]->perform(&to_string));
1380
+ std::string thisSelector((*this)[i]->to_string(ctx.c_options));
1344
1381
  for (size_t j = 0, M = rhs->length(); j < M; ++j)
1345
1382
  {
1346
- if (thisSelector == (*rhs)[j]->perform(&to_string))
1383
+ if (thisSelector == (*rhs)[j]->to_string(ctx.c_options))
1347
1384
  {
1348
1385
  found = true;
1349
1386
  break;
@@ -1362,6 +1399,36 @@ namespace Sass {
1362
1399
  }
1363
1400
  }
1364
1401
 
1402
+ Argument* Arguments::get_rest_argument()
1403
+ {
1404
+ Argument* arg = 0;
1405
+ if (this->has_rest_argument()) {
1406
+ for (auto a : this->elements()) {
1407
+ if (a->is_rest_argument()) {
1408
+ arg = a;
1409
+ break;
1410
+ }
1411
+ }
1412
+ }
1413
+
1414
+ return arg;
1415
+ }
1416
+
1417
+ Argument* Arguments::get_keyword_argument()
1418
+ {
1419
+ Argument* arg = 0;
1420
+ if (this->has_keyword_argument()) {
1421
+ for (auto a : this->elements()) {
1422
+ if (a->is_keyword_argument()) {
1423
+ arg = a;
1424
+ break;
1425
+ }
1426
+ }
1427
+ }
1428
+
1429
+ return arg;
1430
+ }
1431
+
1365
1432
  void Arguments::adjust_after_pushing(Argument* a)
1366
1433
  {
1367
1434
  if (!a->name().empty()) {
@@ -1453,7 +1520,13 @@ namespace Sass {
1453
1520
  return u;
1454
1521
  }
1455
1522
 
1456
- bool Number::is_unitless()
1523
+ bool Number::is_valid_css_unit() const
1524
+ {
1525
+ return numerator_units().size() <= 1 &&
1526
+ denominator_units().size() == 0;
1527
+ }
1528
+
1529
+ bool Number::is_unitless() const
1457
1530
  { return numerator_units_.empty() && denominator_units_.empty(); }
1458
1531
 
1459
1532
  void Number::normalize(const std::string& prefered, bool strict)
@@ -1541,10 +1614,128 @@ namespace Sass {
1541
1614
 
1542
1615
  }
1543
1616
 
1544
- void Number::convert(const std::string& prefered, bool strict)
1617
+ // this does not cover all cases (multiple prefered units)
1618
+ double Number::convert_factor(const Number& n) const
1619
+ {
1620
+
1621
+ // first make sure same units cancel each other out
1622
+ // it seems that a map table will fit nicely to do this
1623
+ // we basically construct exponents for each unit class
1624
+ // std::map<std::string, int> exponents;
1625
+ // initialize by summing up occurences in unit vectors
1626
+ // for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[unit_to_class(numerator_units_[i])];
1627
+ // for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[unit_to_class(denominator_units_[i])];
1628
+
1629
+ std::vector<std::string> l_miss_nums(0);
1630
+ std::vector<std::string> l_miss_dens(0);
1631
+ // create copy since we need these for state keeping
1632
+ std::vector<std::string> r_nums(n.numerator_units_);
1633
+ std::vector<std::string> r_dens(n.denominator_units_);
1634
+
1635
+ std::vector<std::string>::const_iterator l_num_it = numerator_units_.begin();
1636
+ std::vector<std::string>::const_iterator l_num_end = numerator_units_.end();
1637
+
1638
+ bool l_unitless = is_unitless();
1639
+ bool r_unitless = n.is_unitless();
1640
+
1641
+ // overall conversion
1642
+ double factor = 1;
1643
+
1644
+ // process all left numerators
1645
+ while (l_num_it != l_num_end)
1646
+ {
1647
+ // get and increment afterwards
1648
+ const std::string l_num = *(l_num_it ++);
1649
+
1650
+ std::vector<std::string>::iterator r_num_it = r_nums.begin();
1651
+ std::vector<std::string>::iterator r_num_end = r_nums.end();
1652
+
1653
+ bool found = false;
1654
+ // search for compatible numerator
1655
+ while (r_num_it != r_num_end)
1656
+ {
1657
+ // get and increment afterwards
1658
+ const std::string r_num = *(r_num_it);
1659
+ // get possible converstion factor for units
1660
+ double conversion = conversion_factor(l_num, r_num, false);
1661
+ // skip incompatible numerator
1662
+ if (conversion == 0) {
1663
+ ++ r_num_it;
1664
+ continue;
1665
+ }
1666
+ // apply to global factor
1667
+ factor *= conversion;
1668
+ // remove item from vector
1669
+ r_nums.erase(r_num_it);
1670
+ // found numerator
1671
+ found = true;
1672
+ break;
1673
+ }
1674
+ // maybe we did not find any
1675
+ // left numerator is leftover
1676
+ if (!found) l_miss_nums.push_back(l_num);
1677
+ }
1678
+
1679
+ std::vector<std::string>::const_iterator l_den_it = denominator_units_.begin();
1680
+ std::vector<std::string>::const_iterator l_den_end = denominator_units_.end();
1681
+
1682
+ // process all left denominators
1683
+ while (l_den_it != l_den_end)
1684
+ {
1685
+ // get and increment afterwards
1686
+ const std::string l_den = *(l_den_it ++);
1687
+
1688
+ std::vector<std::string>::iterator r_den_it = r_dens.begin();
1689
+ std::vector<std::string>::iterator r_den_end = r_dens.end();
1690
+
1691
+ bool found = false;
1692
+ // search for compatible denominator
1693
+ while (r_den_it != r_den_end)
1694
+ {
1695
+ // get and increment afterwards
1696
+ const std::string r_den = *(r_den_it);
1697
+ // get possible converstion factor for units
1698
+ double conversion = conversion_factor(l_den, r_den, false);
1699
+ // skip incompatible denominator
1700
+ if (conversion == 0) {
1701
+ ++ r_den_it;
1702
+ continue;
1703
+ }
1704
+ // apply to global factor
1705
+ factor *= conversion;
1706
+ // remove item from vector
1707
+ r_dens.erase(r_den_it);
1708
+ // found denominator
1709
+ found = true;
1710
+ break;
1711
+ }
1712
+ // maybe we did not find any
1713
+ // left denominator is leftover
1714
+ if (!found) l_miss_dens.push_back(l_den);
1715
+ }
1716
+
1717
+ // check left-overs (ToDo: might cancel out)
1718
+ if (l_miss_nums.size() > 0 && !r_unitless) {
1719
+ throw Exception::IncompatibleUnits(n, *this);
1720
+ }
1721
+ if (l_miss_dens.size() > 0 && !r_unitless) {
1722
+ throw Exception::IncompatibleUnits(n, *this);
1723
+ }
1724
+ if (r_nums.size() > 0 && !l_unitless) {
1725
+ throw Exception::IncompatibleUnits(n, *this);
1726
+ }
1727
+ if (r_dens.size() > 0 && !l_unitless) {
1728
+ throw Exception::IncompatibleUnits(n, *this);
1729
+ }
1730
+
1731
+ return factor;
1732
+ }
1733
+
1734
+ // this does not cover all cases (multiple prefered units)
1735
+ bool Number::convert(const std::string& prefered, bool strict)
1545
1736
  {
1546
- // abort if unit is empty
1547
- if (prefered.empty()) return;
1737
+ // no conversion if unit is empty
1738
+ if (prefered.empty()) return true;
1548
1739
 
1549
1740
  // first make sure same units cancel each other out
1550
1741
  // it seems that a map table will fit nicely to do this
@@ -1626,6 +1817,9 @@ namespace Sass {
1626
1817
  // best precision this way
1627
1818
  value_ *= factor;
1628
1819
 
1820
+ // success?
1821
+ return true;
1822
+
1629
1823
  }
1630
1824
 
1631
1825
  // useful for making one number compatible with another
@@ -1661,6 +1855,12 @@ namespace Sass {
1661
1855
  bool Number::operator== (const Expression& rhs) const
1662
1856
  {
1663
1857
  if (const Number* r = dynamic_cast<const Number*>(&rhs)) {
1858
+ size_t lhs_units = numerator_units_.size() + denominator_units_.size();
1859
+ size_t rhs_units = r->numerator_units_.size() + r->denominator_units_.size();
1860
+ // unitless and only having one unit seems equivalent (will change in future)
1861
+ if (!lhs_units || !rhs_units) {
1862
+ return std::fabs(value() - r->value()) < NUMBER_EPSILON;
1863
+ }
1664
1864
  return (numerator_units_ == r->numerator_units_) &&
1665
1865
  (denominator_units_ == r->denominator_units_) &&
1666
1866
  std::fabs(value() - r->value()) < NUMBER_EPSILON;
@@ -1670,11 +1870,18 @@ namespace Sass {
1670
1870
 
1671
1871
  bool Number::operator< (const Number& rhs) const
1672
1872
  {
1873
+ size_t lhs_units = numerator_units_.size() + denominator_units_.size();
1874
+ size_t rhs_units = rhs.numerator_units_.size() + rhs.denominator_units_.size();
1875
+ // unitless and only having one unit seems equivalent (will change in future)
1876
+ if (!lhs_units || !rhs_units) {
1877
+ return value() < rhs.value();
1878
+ }
1879
+
1673
1880
  Number tmp_r(rhs);
1674
1881
  tmp_r.normalize(find_convertible_unit());
1675
1882
  std::string l_unit(unit());
1676
1883
  std::string r_unit(tmp_r.unit());
1677
- if (!l_unit.empty() && !r_unit.empty() && unit() != tmp_r.unit()) {
1884
+ if (unit() != tmp_r.unit()) {
1678
1885
  error("cannot compare numbers with incompatible units", pstate());
1679
1886
  }
1680
1887
  return value() < tmp_r.value();
@@ -1700,6 +1907,15 @@ namespace Sass {
1700
1907
  return false;
1701
1908
  }
1702
1909
 
1910
+ bool String_Schema::is_left_interpolant(void) const
1911
+ {
1912
+ return length() && first()->is_left_interpolant();
1913
+ }
1914
+ bool String_Schema::is_right_interpolant(void) const
1915
+ {
1916
+ return length() && last()->is_right_interpolant();
1917
+ }
1918
+
1703
1919
  bool String_Schema::operator== (const Expression& rhs) const
1704
1920
  {
1705
1921
  if (const String_Schema* r = dynamic_cast<const String_Schema*>(&rhs)) {
@@ -1789,65 +2005,29 @@ namespace Sass {
1789
2005
  else { return &sass_null; }
1790
2006
  }
1791
2007
 
1792
- std::string Map::to_string(bool compressed, int precision) const
2008
+ bool Binary_Expression::is_left_interpolant(void) const
1793
2009
  {
1794
- std::string res("");
1795
- if (empty()) return res;
1796
- if (is_invisible()) return res;
1797
- bool items_output = false;
1798
- for (auto key : keys()) {
1799
- if (key->is_invisible()) continue;
1800
- if (at(key)->is_invisible()) continue;
1801
- if (items_output) res += compressed ? "," : ", ";
1802
- Value* v_key = dynamic_cast<Value*>(key);
1803
- Value* v_val = dynamic_cast<Value*>(at(key));
1804
- if (v_key) res += v_key->to_string(compressed, precision);
1805
- res += compressed ? ":" : ": ";
1806
- if (v_val) res += v_val->to_string(compressed, precision);
1807
- items_output = true;
1808
- }
1809
- return res;
2010
+ return is_interpolant() || (left() && left()->is_left_interpolant());
1810
2011
  }
1811
-
1812
- std::string List::to_string(bool compressed, int precision) const
2012
+ bool Binary_Expression::is_right_interpolant(void) const
1813
2013
  {
1814
- std::string res("");
1815
- if (empty()) return res;
1816
- if (is_invisible()) return res;
1817
- bool items_output = false;
1818
- std::string sep = separator() == SASS_COMMA ? "," : " ";
1819
- if (!compressed && sep == ",") sep += " ";
1820
- for (size_t i = 0, L = size(); i < L; ++i) {
1821
- Expression* item = (*this)[i];
1822
- if (item->is_invisible()) continue;
1823
- if (items_output) res += sep;
1824
- if (Value* v_val = dynamic_cast<Value*>(item))
1825
- { res += v_val->to_string(compressed, precision); }
1826
- items_output = true;
1827
- }
1828
- return res;
1829
- }
1830
-
1831
- std::string String_Schema::to_string(bool compressed, int precision) const
1832
- {
1833
- std::string res("");
1834
- for (size_t i = 0, L = length(); i < L; ++i) {
1835
- if ((*this)[i]->is_interpolant()) res += "#{";
1836
- if (Value* val = dynamic_cast<Value*>((*this)[i]))
1837
- { res += val->to_string(compressed, precision); }
1838
- if ((*this)[i]->is_interpolant()) res += "}";
1839
- }
1840
- return res;
2014
+ return is_interpolant() || (right() && right()->is_right_interpolant());
1841
2015
  }
1842
2016
 
1843
- std::string Null::to_string(bool compressed, int precision) const
2017
+ std::string AST_Node::to_string(Sass_Inspect_Options opt) const
1844
2018
  {
1845
- return "null";
2019
+ Sass_Output_Options out(opt);
2020
+ Emitter emitter(out);
2021
+ Inspect i(emitter);
2022
+ i.in_declaration = true;
2023
+ // ToDo: inspect should be const
2024
+ const_cast<AST_Node*>(this)->perform(&i);
2025
+ return i.get_buffer();
1846
2026
  }
1847
2027
 
1848
- std::string Boolean::to_string(bool compressed, int precision) const
2028
+ std::string AST_Node::to_string() const
1849
2029
  {
1850
- return value_ ? "true" : "false";
2030
+ return to_string({ NESTED, 5 });
1851
2031
  }
1852
2032
 
1853
2033
  // helper function for serializing colors
@@ -1858,261 +2038,14 @@ namespace Sass {
1858
2038
  else return c;
1859
2039
  }
1860
2040
 
1861
- std::string Color::to_string(bool compressed, int precision) const
1862
- {
1863
- std::stringstream ss;
1864
-
1865
- // original color name
1866
- // maybe an unknown token
1867
- std::string name = disp();
1868
-
1869
- // resolved color
1870
- std::string res_name = name;
1871
-
1872
- double r = Sass::round(cap_channel<0xff>(r_));
1873
- double g = Sass::round(cap_channel<0xff>(g_));
1874
- double b = Sass::round(cap_channel<0xff>(b_));
1875
- double a = cap_channel<1> (a_);
1876
-
1877
- // get color from given name (if one was given at all)
1878
- if (name != "" && name_to_color(name)) {
1879
- const Color* n = name_to_color(name);
1880
- r = Sass::round(cap_channel<0xff>(n->r()));
1881
- g = Sass::round(cap_channel<0xff>(n->g()));
1882
- b = Sass::round(cap_channel<0xff>(n->b()));
1883
- a = cap_channel<1> (n->a());
1884
- }
1885
- // otherwise get the possible resolved color name
1886
- else {
1887
- double numval = r * 0x10000 + g * 0x100 + b;
1888
- if (color_to_name(numval))
1889
- res_name = color_to_name(numval);
1890
- }
1891
-
1892
- std::stringstream hexlet;
1893
- hexlet << '#' << std::setw(1) << std::setfill('0');
1894
- // create a short color hexlet if there is any need for it
1895
- if (compressed && is_color_doublet(r, g, b) && a == 1) {
1896
- hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(r) >> 4);
1897
- hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(g) >> 4);
1898
- hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(b) >> 4);
1899
- } else {
1900
- hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(r);
1901
- hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(g);
1902
- hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(b);
1903
- }
1904
-
1905
- if (compressed && !this->is_delayed()) name = "";
1906
-
1907
- // retain the originally specified color definition if unchanged
1908
- if (name != "") {
1909
- ss << name;
1910
- }
1911
- else if (r == 0 && g == 0 && b == 0 && a == 0) {
1912
- ss << "transparent";
1913
- }
1914
- else if (a >= 1) {
1915
- if (res_name != "") {
1916
- if (compressed && hexlet.str().size() < res_name.size()) {
1917
- ss << hexlet.str();
1918
- } else {
1919
- ss << res_name;
1920
- }
1921
- }
1922
- else {
1923
- ss << hexlet.str();
1924
- }
1925
- }
1926
- else {
1927
- ss << "rgba(";
1928
- ss << static_cast<unsigned long>(r) << ",";
1929
- if (!compressed) ss << " ";
1930
- ss << static_cast<unsigned long>(g) << ",";
1931
- if (!compressed) ss << " ";
1932
- ss << static_cast<unsigned long>(b) << ",";
1933
- if (!compressed) ss << " ";
1934
- ss << a << ')';
1935
- }
1936
-
1937
- return ss.str();
1938
-
1939
- }
1940
-
1941
- std::string Number::to_string(bool compressed, int precision) const
1942
- {
1943
-
1944
- std::string res;
1945
-
1946
- // check if the fractional part of the value equals to zero
1947
- // neat trick from http://stackoverflow.com/a/1521682/1550314
1948
- // double int_part; bool is_int = modf(value, &int_part) == 0.0;
1949
-
1950
- // this all cannot be done with one run only, since fixed
1951
- // output differs from normal output and regular output
1952
- // can contain scientific notation which we do not want!
1953
-
1954
- // first sample
1955
- std::stringstream ss;
1956
- ss.precision(12);
1957
- ss << value_;
1958
-
1959
- // check if we got scientific notation in result
1960
- if (ss.str().find_first_of("e") != std::string::npos) {
1961
- ss.clear(); ss.str(std::string());
1962
- ss.precision(std::max(12, precision));
1963
- ss << std::fixed << value_;
1964
- }
1965
-
1966
- std::string tmp = ss.str();
1967
- size_t pos_point = tmp.find_first_of(".,");
1968
- size_t pos_fract = tmp.find_last_not_of("0");
1969
- bool is_int = pos_point == pos_fract ||
1970
- pos_point == std::string::npos;
1971
-
1972
- // reset stream for another run
1973
- ss.clear(); ss.str(std::string());
1974
-
1975
- // take a shortcut for integers
1976
- if (is_int)
1977
- {
1978
- ss.precision(0);
1979
- ss << std::fixed << value_;
1980
- res = std::string(ss.str());
1981
- }
1982
- // process floats
1983
- else
1984
- {
1985
- // do we have have too much precision?
1986
- if (pos_fract < precision + pos_point)
1987
- { precision = (int)(pos_fract - pos_point); }
1988
- // round value again
1989
- ss.precision(precision);
1990
- ss << std::fixed << value_;
1991
- res = std::string(ss.str());
1992
- // maybe we truncated up to decimal point
1993
- size_t pos = res.find_last_not_of("0");
1994
- bool at_dec_point = res[pos] == '.' ||
1995
- res[pos] == ',';
1996
- // don't leave a blank point
1997
- if (at_dec_point) ++ pos;
1998
- res.resize (pos + 1);
1999
- }
2000
-
2001
- // some final cosmetics
2002
- if (res == "-0.0") res.erase(0, 1);
2003
- else if (res == "-0") res.erase(0, 1);
2004
- else if (res == "") res = "0";
2005
-
2006
- // add unit now
2007
- res += unit();
2008
-
2009
- // and return
2010
- return res;
2011
-
2012
- }
2013
-
2014
- std::string String_Quoted::to_string(bool compressed, int precision) const
2015
- {
2016
- return quote_mark_ ? quote(value_, quote_mark_, true) : value_;
2017
- }
2018
-
2019
- std::string String_Constant::to_string(bool compressed, int precision) const
2020
- {
2021
- return quote_mark_ ? quote(value_, quote_mark_, true) : value_;
2022
- }
2023
-
2024
- std::string Custom_Error::to_string(bool compressed, int precision) const
2025
- {
2026
- return message();
2027
- }
2028
- std::string Custom_Warning::to_string(bool compressed, int precision) const
2029
- {
2030
- return message();
2031
- }
2032
-
2033
- std::string Selector_List::to_string(bool compressed, int precision) const
2034
- {
2035
- std::string str("");
2036
- auto end = this->end();
2037
- auto start = this->begin();
2038
- while (start < end && *start) {
2039
- Complex_Selector* sel = *start;
2040
- if (!str.empty()) str += ", ";
2041
- str += sel->to_string(compressed, precision);
2042
- ++ start;
2043
- }
2044
- return str;
2045
- }
2046
-
2047
- std::string Compound_Selector::to_string(bool compressed, int precision) const
2048
- {
2049
- std::string str("");
2050
- auto end = this->end();
2051
- auto start = this->begin();
2052
- while (start < end && *start) {
2053
- Simple_Selector* sel = *start;
2054
- str += sel->to_string(compressed, precision);
2055
- ++ start;
2056
- }
2057
- return str;
2058
- }
2059
-
2060
- std::string Complex_Selector::to_string(bool compressed, int precision) const
2061
- {
2062
- // first render head and tail if they are available
2063
- std::string str_head(head() ? head()->to_string(compressed, precision) : "");
2064
- std::string str_tail(tail() ? tail()->to_string(compressed, precision) : "");
2065
- std::string str_ref(reference() ? reference()->to_string(compressed, precision) : "");
2066
- // combinator in between
2067
- std::string str_op("");
2068
- // use a switch statement
2069
- switch (combinator()) {
2070
- case ANCESTOR_OF: str_op = " "; break;
2071
- case PARENT_OF: str_op = ">"; break;
2072
- case PRECEDES: str_op = "~"; break;
2073
- case ADJACENT_TO: str_op = "+"; break;
2074
- case REFERENCE: str_op = "/" + str_ref + "/"; break;
2075
- }
2076
- // prettify for non ancestors
2077
- if (combinator() != ANCESTOR_OF) {
2078
- // no spaces needed for compressed
2079
- if (compressed == false) {
2080
- // make sure we add some spaces where needed
2081
- if (str_tail != "") str_op += " ";
2082
- if (str_head != "") str_head += " ";
2083
- }
2084
- }
2085
- // is ancestor with no tail
2086
- else if (str_tail == "") {
2087
- str_op = ""; // superflous
2088
- }
2089
- // now build the final result
2090
- return str_head + str_op + str_tail;
2091
- }
2092
-
2093
- std::string Selector_Schema::to_string(bool compressed, int precision) const
2094
- {
2095
- return contents()->to_string(compressed, precision);
2096
- }
2097
-
2098
- std::string Parent_Selector::to_string(bool compressed, int precision) const
2099
- {
2100
- return "&";
2101
- }
2102
-
2103
- std::string Attribute_Selector::to_string(bool compressed, int precision) const
2041
+ std::string String_Quoted::inspect() const
2104
2042
  {
2105
- std::string val(value() ? value()->to_string(compressed, precision) : "");
2106
- return "[" + this->ns_name() + this->matcher() + val + "]";
2043
+ return quote(value_, '*');
2107
2044
  }
2108
2045
 
2109
- std::string Wrapped_Selector::to_string(bool compressed, int precision) const
2046
+ std::string String_Constant::inspect() const
2110
2047
  {
2111
- // first render the
2112
- std::string main(this->Simple_Selector::to_string(compressed, precision));
2113
- std::string wrapped(selector() ? selector()->to_string(compressed, precision) : "");
2114
- // now build the final result
2115
- return main + "(" + wrapped + ")";
2048
+ return quote(value_, '*');
2116
2049
  }
2117
2050
 
2118
2051
  //////////////////////////////////////////////////////////////////////////////////////////