sassc4 2.4.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 (216) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +16 -0
  5. data/CHANGELOG.md +97 -0
  6. data/CODE_OF_CONDUCT.md +10 -0
  7. data/Gemfile +2 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +80 -0
  10. data/Rakefile +51 -0
  11. data/ext/depend +4 -0
  12. data/ext/extconf.rb +92 -0
  13. data/ext/libsass/VERSION +1 -0
  14. data/ext/libsass/contrib/plugin.cpp +60 -0
  15. data/ext/libsass/include/sass/base.h +97 -0
  16. data/ext/libsass/include/sass/context.h +174 -0
  17. data/ext/libsass/include/sass/functions.h +139 -0
  18. data/ext/libsass/include/sass/values.h +145 -0
  19. data/ext/libsass/include/sass/version.h +12 -0
  20. data/ext/libsass/include/sass.h +15 -0
  21. data/ext/libsass/include/sass2scss.h +120 -0
  22. data/ext/libsass/src/MurmurHash2.hpp +91 -0
  23. data/ext/libsass/src/ast.cpp +953 -0
  24. data/ext/libsass/src/ast.hpp +1064 -0
  25. data/ext/libsass/src/ast2c.cpp +80 -0
  26. data/ext/libsass/src/ast2c.hpp +39 -0
  27. data/ext/libsass/src/ast_def_macros.hpp +140 -0
  28. data/ext/libsass/src/ast_fwd_decl.cpp +31 -0
  29. data/ext/libsass/src/ast_fwd_decl.hpp +274 -0
  30. data/ext/libsass/src/ast_helpers.hpp +316 -0
  31. data/ext/libsass/src/ast_sel_cmp.cpp +396 -0
  32. data/ext/libsass/src/ast_sel_super.cpp +539 -0
  33. data/ext/libsass/src/ast_sel_unify.cpp +275 -0
  34. data/ext/libsass/src/ast_sel_weave.cpp +616 -0
  35. data/ext/libsass/src/ast_selectors.cpp +1070 -0
  36. data/ext/libsass/src/ast_selectors.hpp +523 -0
  37. data/ext/libsass/src/ast_supports.cpp +114 -0
  38. data/ext/libsass/src/ast_supports.hpp +121 -0
  39. data/ext/libsass/src/ast_values.cpp +1154 -0
  40. data/ext/libsass/src/ast_values.hpp +498 -0
  41. data/ext/libsass/src/b64/cencode.h +32 -0
  42. data/ext/libsass/src/b64/encode.h +79 -0
  43. data/ext/libsass/src/backtrace.cpp +50 -0
  44. data/ext/libsass/src/backtrace.hpp +29 -0
  45. data/ext/libsass/src/base64vlq.cpp +47 -0
  46. data/ext/libsass/src/base64vlq.hpp +30 -0
  47. data/ext/libsass/src/bind.cpp +312 -0
  48. data/ext/libsass/src/bind.hpp +15 -0
  49. data/ext/libsass/src/c2ast.cpp +64 -0
  50. data/ext/libsass/src/c2ast.hpp +14 -0
  51. data/ext/libsass/src/c99func.c +54 -0
  52. data/ext/libsass/src/cencode.c +106 -0
  53. data/ext/libsass/src/check_nesting.cpp +393 -0
  54. data/ext/libsass/src/check_nesting.hpp +70 -0
  55. data/ext/libsass/src/color_maps.cpp +652 -0
  56. data/ext/libsass/src/color_maps.hpp +323 -0
  57. data/ext/libsass/src/color_spaces.cpp +241 -0
  58. data/ext/libsass/src/color_spaces.hpp +227 -0
  59. data/ext/libsass/src/constants.cpp +199 -0
  60. data/ext/libsass/src/constants.hpp +200 -0
  61. data/ext/libsass/src/context.cpp +870 -0
  62. data/ext/libsass/src/context.hpp +140 -0
  63. data/ext/libsass/src/cssize.cpp +521 -0
  64. data/ext/libsass/src/cssize.hpp +71 -0
  65. data/ext/libsass/src/dart_helpers.hpp +199 -0
  66. data/ext/libsass/src/debug.hpp +43 -0
  67. data/ext/libsass/src/debugger.hpp +964 -0
  68. data/ext/libsass/src/emitter.cpp +297 -0
  69. data/ext/libsass/src/emitter.hpp +101 -0
  70. data/ext/libsass/src/environment.cpp +260 -0
  71. data/ext/libsass/src/environment.hpp +124 -0
  72. data/ext/libsass/src/error_handling.cpp +239 -0
  73. data/ext/libsass/src/error_handling.hpp +248 -0
  74. data/ext/libsass/src/eval.cpp +1543 -0
  75. data/ext/libsass/src/eval.hpp +110 -0
  76. data/ext/libsass/src/eval_selectors.cpp +75 -0
  77. data/ext/libsass/src/expand.cpp +875 -0
  78. data/ext/libsass/src/expand.hpp +98 -0
  79. data/ext/libsass/src/extender.cpp +1226 -0
  80. data/ext/libsass/src/extender.hpp +399 -0
  81. data/ext/libsass/src/extension.cpp +43 -0
  82. data/ext/libsass/src/extension.hpp +89 -0
  83. data/ext/libsass/src/file.cpp +531 -0
  84. data/ext/libsass/src/file.hpp +124 -0
  85. data/ext/libsass/src/fn_colors.cpp +836 -0
  86. data/ext/libsass/src/fn_colors.hpp +99 -0
  87. data/ext/libsass/src/fn_lists.cpp +285 -0
  88. data/ext/libsass/src/fn_lists.hpp +34 -0
  89. data/ext/libsass/src/fn_maps.cpp +94 -0
  90. data/ext/libsass/src/fn_maps.hpp +30 -0
  91. data/ext/libsass/src/fn_miscs.cpp +248 -0
  92. data/ext/libsass/src/fn_miscs.hpp +40 -0
  93. data/ext/libsass/src/fn_numbers.cpp +246 -0
  94. data/ext/libsass/src/fn_numbers.hpp +45 -0
  95. data/ext/libsass/src/fn_selectors.cpp +205 -0
  96. data/ext/libsass/src/fn_selectors.hpp +35 -0
  97. data/ext/libsass/src/fn_strings.cpp +268 -0
  98. data/ext/libsass/src/fn_strings.hpp +34 -0
  99. data/ext/libsass/src/fn_utils.cpp +159 -0
  100. data/ext/libsass/src/fn_utils.hpp +62 -0
  101. data/ext/libsass/src/inspect.cpp +1126 -0
  102. data/ext/libsass/src/inspect.hpp +101 -0
  103. data/ext/libsass/src/json.cpp +1436 -0
  104. data/ext/libsass/src/json.hpp +117 -0
  105. data/ext/libsass/src/kwd_arg_macros.hpp +28 -0
  106. data/ext/libsass/src/lexer.cpp +122 -0
  107. data/ext/libsass/src/lexer.hpp +304 -0
  108. data/ext/libsass/src/listize.cpp +70 -0
  109. data/ext/libsass/src/listize.hpp +37 -0
  110. data/ext/libsass/src/mapping.hpp +19 -0
  111. data/ext/libsass/src/memory/allocator.cpp +48 -0
  112. data/ext/libsass/src/memory/allocator.hpp +138 -0
  113. data/ext/libsass/src/memory/config.hpp +20 -0
  114. data/ext/libsass/src/memory/memory_pool.hpp +186 -0
  115. data/ext/libsass/src/memory/shared_ptr.cpp +33 -0
  116. data/ext/libsass/src/memory/shared_ptr.hpp +332 -0
  117. data/ext/libsass/src/memory.hpp +12 -0
  118. data/ext/libsass/src/operation.hpp +223 -0
  119. data/ext/libsass/src/operators.cpp +267 -0
  120. data/ext/libsass/src/operators.hpp +30 -0
  121. data/ext/libsass/src/ordered_map.hpp +112 -0
  122. data/ext/libsass/src/output.cpp +320 -0
  123. data/ext/libsass/src/output.hpp +47 -0
  124. data/ext/libsass/src/parser.cpp +3059 -0
  125. data/ext/libsass/src/parser.hpp +395 -0
  126. data/ext/libsass/src/parser_selectors.cpp +189 -0
  127. data/ext/libsass/src/permutate.hpp +164 -0
  128. data/ext/libsass/src/plugins.cpp +188 -0
  129. data/ext/libsass/src/plugins.hpp +57 -0
  130. data/ext/libsass/src/position.cpp +163 -0
  131. data/ext/libsass/src/position.hpp +147 -0
  132. data/ext/libsass/src/prelexer.cpp +1780 -0
  133. data/ext/libsass/src/prelexer.hpp +484 -0
  134. data/ext/libsass/src/remove_placeholders.cpp +86 -0
  135. data/ext/libsass/src/remove_placeholders.hpp +37 -0
  136. data/ext/libsass/src/sass.cpp +156 -0
  137. data/ext/libsass/src/sass.hpp +147 -0
  138. data/ext/libsass/src/sass2scss.cpp +895 -0
  139. data/ext/libsass/src/sass_context.cpp +742 -0
  140. data/ext/libsass/src/sass_context.hpp +129 -0
  141. data/ext/libsass/src/sass_functions.cpp +210 -0
  142. data/ext/libsass/src/sass_functions.hpp +50 -0
  143. data/ext/libsass/src/sass_values.cpp +362 -0
  144. data/ext/libsass/src/sass_values.hpp +82 -0
  145. data/ext/libsass/src/settings.hpp +19 -0
  146. data/ext/libsass/src/source.cpp +69 -0
  147. data/ext/libsass/src/source.hpp +95 -0
  148. data/ext/libsass/src/source_data.hpp +32 -0
  149. data/ext/libsass/src/source_map.cpp +202 -0
  150. data/ext/libsass/src/source_map.hpp +65 -0
  151. data/ext/libsass/src/stylesheet.cpp +22 -0
  152. data/ext/libsass/src/stylesheet.hpp +57 -0
  153. data/ext/libsass/src/to_value.cpp +114 -0
  154. data/ext/libsass/src/to_value.hpp +46 -0
  155. data/ext/libsass/src/units.cpp +507 -0
  156. data/ext/libsass/src/units.hpp +110 -0
  157. data/ext/libsass/src/utf8/checked.h +336 -0
  158. data/ext/libsass/src/utf8/core.h +332 -0
  159. data/ext/libsass/src/utf8/unchecked.h +235 -0
  160. data/ext/libsass/src/utf8.h +34 -0
  161. data/ext/libsass/src/utf8_string.cpp +104 -0
  162. data/ext/libsass/src/utf8_string.hpp +38 -0
  163. data/ext/libsass/src/util.cpp +723 -0
  164. data/ext/libsass/src/util.hpp +105 -0
  165. data/ext/libsass/src/util_string.cpp +125 -0
  166. data/ext/libsass/src/util_string.hpp +73 -0
  167. data/ext/libsass/src/values.cpp +140 -0
  168. data/ext/libsass/src/values.hpp +12 -0
  169. data/lib/sassc/dependency.rb +17 -0
  170. data/lib/sassc/engine.rb +141 -0
  171. data/lib/sassc/error.rb +37 -0
  172. data/lib/sassc/functions_handler.rb +73 -0
  173. data/lib/sassc/import_handler.rb +50 -0
  174. data/lib/sassc/importer.rb +31 -0
  175. data/lib/sassc/native/native_context_api.rb +147 -0
  176. data/lib/sassc/native/native_functions_api.rb +159 -0
  177. data/lib/sassc/native/sass2scss_api.rb +10 -0
  178. data/lib/sassc/native/sass_input_style.rb +13 -0
  179. data/lib/sassc/native/sass_output_style.rb +12 -0
  180. data/lib/sassc/native/sass_value.rb +97 -0
  181. data/lib/sassc/native/string_list.rb +10 -0
  182. data/lib/sassc/native.rb +64 -0
  183. data/lib/sassc/sass_2_scss.rb +9 -0
  184. data/lib/sassc/script/functions.rb +8 -0
  185. data/lib/sassc/script/value/bool.rb +32 -0
  186. data/lib/sassc/script/value/color.rb +95 -0
  187. data/lib/sassc/script/value/list.rb +136 -0
  188. data/lib/sassc/script/value/map.rb +69 -0
  189. data/lib/sassc/script/value/number.rb +389 -0
  190. data/lib/sassc/script/value/string.rb +96 -0
  191. data/lib/sassc/script/value.rb +137 -0
  192. data/lib/sassc/script/value_conversion/base.rb +13 -0
  193. data/lib/sassc/script/value_conversion/bool.rb +13 -0
  194. data/lib/sassc/script/value_conversion/color.rb +18 -0
  195. data/lib/sassc/script/value_conversion/list.rb +25 -0
  196. data/lib/sassc/script/value_conversion/map.rb +21 -0
  197. data/lib/sassc/script/value_conversion/number.rb +13 -0
  198. data/lib/sassc/script/value_conversion/string.rb +17 -0
  199. data/lib/sassc/script/value_conversion.rb +69 -0
  200. data/lib/sassc/script.rb +17 -0
  201. data/lib/sassc/util/normalized_map.rb +117 -0
  202. data/lib/sassc/util.rb +231 -0
  203. data/lib/sassc/version.rb +5 -0
  204. data/lib/sassc.rb +57 -0
  205. data/sassc.gemspec +69 -0
  206. data/test/css_color_level4_test.rb +168 -0
  207. data/test/custom_importer_test.rb +127 -0
  208. data/test/engine_test.rb +314 -0
  209. data/test/error_test.rb +29 -0
  210. data/test/fixtures/paths.scss +10 -0
  211. data/test/functions_test.rb +340 -0
  212. data/test/native_test.rb +213 -0
  213. data/test/output_style_test.rb +107 -0
  214. data/test/sass_2_scss_test.rb +14 -0
  215. data/test/test_helper.rb +45 -0
  216. metadata +396 -0
@@ -0,0 +1,895 @@
1
+ /**
2
+ * sass2scss
3
+ * Licensed under the MIT License
4
+ * Copyright (c) Marcel Greter
5
+ */
6
+
7
+ #ifdef _MSC_VER
8
+ #define _CRT_SECURE_NO_WARNINGS
9
+ #define _CRT_NONSTDC_NO_DEPRECATE
10
+ #endif
11
+
12
+ // include library
13
+ #include <stack>
14
+ #include <string>
15
+ #include <cstring>
16
+ #include <cstdlib>
17
+ #include <sstream>
18
+ #include <iostream>
19
+ #include <stdio.h>
20
+
21
+ ///*
22
+ //
23
+ // src comments: comments in sass syntax (staring with //)
24
+ // css comments: multiline comments in css syntax (starting with /*)
25
+ //
26
+ // KEEP_COMMENT: keep src comments in the resulting css code
27
+ // STRIP_COMMENT: strip out all comments (either src or css)
28
+ // CONVERT_COMMENT: convert all src comments to css comments
29
+ //
30
+ //*/
31
+
32
+ // our own header
33
+ #include "sass2scss.h"
34
+
35
+ // add namespace for c++
36
+ namespace Sass
37
+ {
38
+
39
+ // return the actual prettify value from options
40
+ #define PRETTIFY(converter) (converter.options - (converter.options & 248))
41
+ // query the options integer to check if the option is enables
42
+ #define KEEP_COMMENT(converter) ((converter.options & SASS2SCSS_KEEP_COMMENT) == SASS2SCSS_KEEP_COMMENT)
43
+ #define STRIP_COMMENT(converter) ((converter.options & SASS2SCSS_STRIP_COMMENT) == SASS2SCSS_STRIP_COMMENT)
44
+ #define CONVERT_COMMENT(converter) ((converter.options & SASS2SCSS_CONVERT_COMMENT) == SASS2SCSS_CONVERT_COMMENT)
45
+
46
+ // some makros to access the indentation stack
47
+ #define INDENT(converter) (converter.indents.top())
48
+
49
+ // some makros to query comment parser status
50
+ #define IS_PARSING(converter) (converter.comment == "")
51
+ #define IS_COMMENT(converter) (converter.comment != "")
52
+ #define IS_SRC_COMMENT(converter) (converter.comment == "//" && ! CONVERT_COMMENT(converter))
53
+ #define IS_CSS_COMMENT(converter) (converter.comment == "/*" || (converter.comment == "//" && CONVERT_COMMENT(converter)))
54
+
55
+ // pretty printer helper function
56
+ static std::string closer (const converter& converter)
57
+ {
58
+ return PRETTIFY(converter) == 0 ? " }" :
59
+ PRETTIFY(converter) <= 1 ? " }" :
60
+ "\n" + INDENT(converter) + "}";
61
+ }
62
+
63
+ // pretty printer helper function
64
+ static std::string opener (const converter& converter)
65
+ {
66
+ return PRETTIFY(converter) == 0 ? " { " :
67
+ PRETTIFY(converter) <= 2 ? " {" :
68
+ "\n" + INDENT(converter) + "{";
69
+ }
70
+
71
+ // check if the given string is a pseudo selector
72
+ // needed to differentiate from sass property syntax
73
+ static bool isPseudoSelector (std::string& sel)
74
+ {
75
+
76
+ size_t len = sel.length();
77
+ if (len < 1) return false;
78
+ size_t pos = sel.find_first_not_of("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1);
79
+ if (pos != std::string::npos) sel.erase(pos, std::string::npos);
80
+ size_t i = sel.length();
81
+ while (i -- > 0) { sel.at(i) = tolower(sel.at(i)); }
82
+
83
+ // CSS Level 1 - Recommendation
84
+ if (sel == ":link") return true;
85
+ if (sel == ":visited") return true;
86
+ if (sel == ":active") return true;
87
+
88
+ // CSS Level 2 (Revision 1) - Recommendation
89
+ if (sel == ":lang") return true;
90
+ if (sel == ":first-child") return true;
91
+ if (sel == ":hover") return true;
92
+ if (sel == ":focus") return true;
93
+ // disabled - also valid properties
94
+ // if (sel == ":left") return true;
95
+ // if (sel == ":right") return true;
96
+ if (sel == ":first") return true;
97
+
98
+ // Selectors Level 3 - Recommendation
99
+ if (sel == ":target") return true;
100
+ if (sel == ":root") return true;
101
+ if (sel == ":nth-child") return true;
102
+ if (sel == ":nth-last-of-child") return true;
103
+ if (sel == ":nth-of-type") return true;
104
+ if (sel == ":nth-last-of-type") return true;
105
+ if (sel == ":last-child") return true;
106
+ if (sel == ":first-of-type") return true;
107
+ if (sel == ":last-of-type") return true;
108
+ if (sel == ":only-child") return true;
109
+ if (sel == ":only-of-type") return true;
110
+ if (sel == ":empty") return true;
111
+ if (sel == ":not") return true;
112
+
113
+ // CSS Basic User Interface Module Level 3 - Working Draft
114
+ if (sel == ":default") return true;
115
+ if (sel == ":valid") return true;
116
+ if (sel == ":invalid") return true;
117
+ if (sel == ":in-range") return true;
118
+ if (sel == ":out-of-range") return true;
119
+ if (sel == ":required") return true;
120
+ if (sel == ":optional") return true;
121
+ if (sel == ":read-only") return true;
122
+ if (sel == ":read-write") return true;
123
+ if (sel == ":dir") return true;
124
+ if (sel == ":enabled") return true;
125
+ if (sel == ":disabled") return true;
126
+ if (sel == ":checked") return true;
127
+ if (sel == ":indeterminate") return true;
128
+ if (sel == ":nth-last-child") return true;
129
+
130
+ // Selectors Level 4 - Working Draft
131
+ if (sel == ":any-link") return true;
132
+ if (sel == ":local-link") return true;
133
+ if (sel == ":scope") return true;
134
+ if (sel == ":active-drop-target") return true;
135
+ if (sel == ":valid-drop-target") return true;
136
+ if (sel == ":invalid-drop-target") return true;
137
+ if (sel == ":current") return true;
138
+ if (sel == ":past") return true;
139
+ if (sel == ":future") return true;
140
+ if (sel == ":placeholder-shown") return true;
141
+ if (sel == ":user-error") return true;
142
+ if (sel == ":blank") return true;
143
+ if (sel == ":nth-match") return true;
144
+ if (sel == ":nth-last-match") return true;
145
+ if (sel == ":nth-column") return true;
146
+ if (sel == ":nth-last-column") return true;
147
+ if (sel == ":matches") return true;
148
+
149
+ // Fullscreen API - Living Standard
150
+ if (sel == ":fullscreen") return true;
151
+
152
+ // not a pseudo selector
153
+ return false;
154
+
155
+ }
156
+
157
+ static size_t findFirstCharacter (std::string& sass, size_t pos)
158
+ {
159
+ return sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos);
160
+ }
161
+
162
+ static size_t findLastCharacter (std::string& sass, size_t pos)
163
+ {
164
+ return sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE, pos);
165
+ }
166
+
167
+ static bool isUrl (std::string& sass, size_t pos)
168
+ {
169
+ return sass[pos] == 'u' && sass[pos+1] == 'r' && sass[pos+2] == 'l' && sass[pos+3] == '(';
170
+ }
171
+
172
+ // check if there is some char data
173
+ // will ignore everything in comments
174
+ static bool hasCharData (std::string& sass)
175
+ {
176
+
177
+ size_t col_pos = 0;
178
+
179
+ while (true)
180
+ {
181
+
182
+ // try to find some meaningful char
183
+ col_pos = sass.find_first_not_of(" \t\n\v\f\r", col_pos);
184
+
185
+ // there was no meaningful char found
186
+ if (col_pos == std::string::npos) return false;
187
+
188
+ // found a multiline comment opener
189
+ if (sass.substr(col_pos, 2) == "/*")
190
+ {
191
+ // find the multiline comment closer
192
+ col_pos = sass.find("*/", col_pos);
193
+ // maybe we did not find the closer here
194
+ if (col_pos == std::string::npos) return false;
195
+ // skip closer
196
+ col_pos += 2;
197
+ }
198
+ else
199
+ {
200
+ return true;
201
+ }
202
+
203
+ }
204
+
205
+ }
206
+ // EO hasCharData
207
+
208
+ // find src comment opener
209
+ // correctly skips quoted strings
210
+ static size_t findCommentOpener (std::string& sass)
211
+ {
212
+
213
+ size_t col_pos = 0;
214
+ bool apoed = false;
215
+ bool quoted = false;
216
+ bool comment = false;
217
+ size_t brackets = 0;
218
+
219
+ while (col_pos != std::string::npos)
220
+ {
221
+
222
+ // process all interesting chars
223
+ col_pos = sass.find_first_of("\"\'/\\*()", col_pos);
224
+
225
+ // assertion for valid result
226
+ if (col_pos != std::string::npos)
227
+ {
228
+ char character = sass.at(col_pos);
229
+
230
+ if (character == '(')
231
+ {
232
+ if (!quoted && !apoed) brackets ++;
233
+ }
234
+ else if (character == ')')
235
+ {
236
+ if (!quoted && !apoed) brackets --;
237
+ }
238
+ else if (character == '\"')
239
+ {
240
+ // invert quote bool
241
+ if (!apoed && !comment) quoted = !quoted;
242
+ }
243
+ else if (character == '\'')
244
+ {
245
+ // invert quote bool
246
+ if (!quoted && !comment) apoed = !apoed;
247
+ }
248
+ else if (col_pos > 0 && character == '/')
249
+ {
250
+ if (sass.at(col_pos - 1) == '*')
251
+ {
252
+ comment = false;
253
+ }
254
+ // next needs to be a slash too
255
+ else if (sass.at(col_pos - 1) == '/')
256
+ {
257
+ // only found if not in single or double quote, bracket or comment
258
+ if (!quoted && !apoed && !comment && brackets == 0) return col_pos - 1;
259
+ }
260
+ }
261
+ else if (character == '\\')
262
+ {
263
+ // skip next char if in quote
264
+ if (quoted || apoed) col_pos ++;
265
+ }
266
+ // this might be a comment opener
267
+ else if (col_pos > 0 && character == '*')
268
+ {
269
+ // opening a multiline comment
270
+ if (sass.at(col_pos - 1) == '/')
271
+ {
272
+ // we are now in a comment
273
+ if (!quoted && !apoed) comment = true;
274
+ }
275
+ }
276
+
277
+ // skip char
278
+ col_pos ++;
279
+
280
+ }
281
+
282
+ }
283
+ // EO while
284
+
285
+ return col_pos;
286
+
287
+ }
288
+ // EO findCommentOpener
289
+
290
+ // remove multiline comments from sass string
291
+ // correctly skips quoted strings
292
+ static std::string removeMultilineComment (std::string &sass)
293
+ {
294
+
295
+ std::string clean = "";
296
+ size_t col_pos = 0;
297
+ size_t open_pos = 0;
298
+ size_t close_pos = 0;
299
+ bool apoed = false;
300
+ bool quoted = false;
301
+ bool comment = false;
302
+
303
+ // process sass til string end
304
+ while (col_pos != std::string::npos)
305
+ {
306
+
307
+ // process all interesting chars
308
+ col_pos = sass.find_first_of("\"\'/\\*", col_pos);
309
+
310
+ // assertion for valid result
311
+ if (col_pos != std::string::npos)
312
+ {
313
+ char character = sass.at(col_pos);
314
+
315
+ // found quoted string delimiter
316
+ if (character == '\"')
317
+ {
318
+ if (!apoed && !comment) quoted = !quoted;
319
+ }
320
+ else if (character == '\'')
321
+ {
322
+ if (!quoted && !comment) apoed = !apoed;
323
+ }
324
+ // found possible comment closer
325
+ else if (character == '/')
326
+ {
327
+ // look back to see if it is actually a closer
328
+ if (comment && col_pos > 0 && sass.at(col_pos - 1) == '*')
329
+ {
330
+ close_pos = col_pos + 1; comment = false;
331
+ }
332
+ }
333
+ else if (character == '\\')
334
+ {
335
+ // skip escaped char
336
+ if (quoted || apoed) col_pos ++;
337
+ }
338
+ // this might be a comment opener
339
+ else if (character == '*')
340
+ {
341
+ // look back to see if it is actually an opener
342
+ if (!quoted && !apoed && col_pos > 0 && sass.at(col_pos - 1) == '/')
343
+ {
344
+ comment = true; open_pos = col_pos - 1;
345
+ clean += sass.substr(close_pos, open_pos - close_pos);
346
+ }
347
+ }
348
+
349
+ // skip char
350
+ col_pos ++;
351
+
352
+ }
353
+
354
+ }
355
+ // EO while
356
+
357
+ // add final parts (add half open comment text)
358
+ if (comment) clean += sass.substr(open_pos);
359
+ else clean += sass.substr(close_pos);
360
+
361
+ // return string
362
+ return clean;
363
+
364
+ }
365
+ // EO removeMultilineComment
366
+
367
+ // right trim a given string
368
+ std::string rtrim(const std::string &sass)
369
+ {
370
+ std::string trimmed = sass;
371
+ size_t pos_ws = trimmed.find_last_not_of(" \t\n\v\f\r");
372
+ if (pos_ws != std::string::npos)
373
+ { trimmed.erase(pos_ws + 1); }
374
+ else { trimmed.clear(); }
375
+ return trimmed;
376
+ }
377
+ // EO rtrim
378
+
379
+ // flush whitespace and print additional text, but
380
+ // only print additional chars and buffer whitespace
381
+ std::string flush (std::string& sass, converter& converter)
382
+ {
383
+
384
+ // return flushed
385
+ std::string scss = "";
386
+
387
+ // print whitespace buffer
388
+ scss += PRETTIFY(converter) > 0 ?
389
+ converter.whitespace : "";
390
+ // reset whitespace buffer
391
+ converter.whitespace = "";
392
+
393
+ // remove possible newlines from string
394
+ size_t pos_right = sass.find_last_not_of("\n\r");
395
+ if (pos_right == std::string::npos) return scss;
396
+
397
+ // get the linefeeds from the string
398
+ std::string lfs = sass.substr(pos_right + 1);
399
+ sass = sass.substr(0, pos_right + 1);
400
+
401
+ // find some source comment opener
402
+ size_t comment_pos = findCommentOpener(sass);
403
+ // check if there was a source comment
404
+ if (comment_pos != std::string::npos)
405
+ {
406
+ // convert comment (but only outside other coments)
407
+ if (CONVERT_COMMENT(converter) && !IS_COMMENT(converter))
408
+ {
409
+ // convert to multiline comment
410
+ sass.at(comment_pos + 1) = '*';
411
+ // add comment node to the whitespace
412
+ sass += " */";
413
+ }
414
+ // not at line start
415
+ if (comment_pos > 0)
416
+ {
417
+ // also include whitespace before the actual comment opener
418
+ size_t ws_pos = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE, comment_pos - 1);
419
+ comment_pos = ws_pos == std::string::npos ? 0 : ws_pos + 1;
420
+ }
421
+ if (!STRIP_COMMENT(converter))
422
+ {
423
+ // add comment node to the whitespace
424
+ converter.whitespace += sass.substr(comment_pos);
425
+ }
426
+ else
427
+ {
428
+ // sass = removeMultilineComments(sass);
429
+ }
430
+ // update the actual sass code
431
+ sass = sass.substr(0, comment_pos);
432
+ }
433
+
434
+ // add newline as getline discharged it
435
+ converter.whitespace += lfs + "\n";
436
+
437
+ // maybe remove any leading whitespace
438
+ if (PRETTIFY(converter) == 0)
439
+ {
440
+ // remove leading whitespace and update string
441
+ size_t pos_left = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE);
442
+ if (pos_left != std::string::npos) sass = sass.substr(pos_left);
443
+ }
444
+
445
+ // add flushed data
446
+ scss += sass;
447
+
448
+ // return string
449
+ return scss;
450
+
451
+ }
452
+ // EO flush
453
+
454
+ // process a line of the sass text
455
+ std::string process (std::string& sass, converter& converter)
456
+ {
457
+
458
+ // resulting string
459
+ std::string scss = "";
460
+
461
+ // strip multi line comments
462
+ if (STRIP_COMMENT(converter))
463
+ {
464
+ sass = removeMultilineComment(sass);
465
+ }
466
+
467
+ // right trim input
468
+ sass = rtrim(sass);
469
+
470
+ // get position of first meaningful character in string
471
+ size_t pos_left = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE);
472
+
473
+ // special case for final run
474
+ if (converter.end_of_file) pos_left = 0;
475
+
476
+ // maybe has only whitespace
477
+ if (pos_left == std::string::npos)
478
+ {
479
+ // just add complete whitespace
480
+ converter.whitespace += sass + "\n";
481
+ }
482
+ // have meaningful first char
483
+ else
484
+ {
485
+
486
+ // extract and store indentation string
487
+ std::string indent = sass.substr(0, pos_left);
488
+
489
+ // check if current line starts a comment
490
+ std::string open = sass.substr(pos_left, 2);
491
+
492
+ // line has less or same indentation
493
+ // finalize previous open parser context
494
+ if (indent.length() <= INDENT(converter).length())
495
+ {
496
+
497
+ // close multilinie comment
498
+ if (IS_CSS_COMMENT(converter))
499
+ {
500
+ // check if comments will be stripped anyway
501
+ if (!STRIP_COMMENT(converter)) scss += " */";
502
+ }
503
+ // close src comment comment
504
+ else if (IS_SRC_COMMENT(converter))
505
+ {
506
+ // add a newline to avoid closer on same line
507
+ // this would put the bracket in the comment node
508
+ // no longer needed since we parse them correctly
509
+ // if (KEEP_COMMENT(converter)) scss += "\n";
510
+ }
511
+ // close css properties
512
+ else if (converter.property)
513
+ {
514
+ // add closer unless in concat mode
515
+ if (!converter.comma)
516
+ {
517
+ // if there was no colon we have a selector
518
+ // looks like there were no inner properties
519
+ if (converter.selector) scss += " {}";
520
+ // add final semicolon
521
+ else if (!converter.semicolon) scss += ";";
522
+ }
523
+ }
524
+
525
+ // reset comment state
526
+ converter.comment = "";
527
+
528
+ }
529
+
530
+ // make sure we close every "higher" block
531
+ while (indent.length() < INDENT(converter).length())
532
+ {
533
+ // pop stacked context
534
+ converter.indents.pop();
535
+ // print close bracket
536
+ if (IS_PARSING(converter))
537
+ { scss += closer(converter); }
538
+ else { scss += " */"; }
539
+ // reset comment state
540
+ converter.comment = "";
541
+ }
542
+
543
+ // reset converter state
544
+ converter.selector = false;
545
+
546
+ // looks like some undocumented behavior ...
547
+ // https://github.com/mgreter/sass2scss/issues/29
548
+ if (sass.substr(pos_left, 1) == "\\") {
549
+ converter.selector = true;
550
+ sass[pos_left] = ' ';
551
+ }
552
+
553
+ // check if we have sass property syntax
554
+ if (sass.substr(pos_left, 1) == ":" && sass.substr(pos_left, 2) != "::")
555
+ {
556
+
557
+ // default to a selector
558
+ // change back if property found
559
+ converter.selector = true;
560
+ // get position of first whitespace char
561
+ size_t pos_wspace = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left);
562
+ // assertion check for valid result
563
+ if (pos_wspace != std::string::npos)
564
+ {
565
+ // get the possible pseudo selector
566
+ std::string pseudo = sass.substr(pos_left, pos_wspace - pos_left);
567
+ // get position of the first real property value char
568
+ // pseudo selectors get this far, but have no actual value
569
+ size_t pos_value = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_wspace);
570
+ // assertion check for valid result
571
+ if (pos_value != std::string::npos)
572
+ {
573
+ // only process if not (fallowed by a semicolon or is a pseudo selector)
574
+ if (!(sass.at(pos_value) == ':' || isPseudoSelector(pseudo)))
575
+ {
576
+ // create new string by interchanging the colon sign for property and value
577
+ sass = indent + sass.substr(pos_left + 1, pos_wspace - pos_left - 1) + ":" + sass.substr(pos_wspace);
578
+ // try to find a colon in the current line, but only ...
579
+ size_t pos_colon = sass.find_first_not_of(":", pos_left);
580
+ // assertion for valid result
581
+ if (pos_colon != std::string::npos)
582
+ {
583
+ // ... after the first word (skip beginning colons)
584
+ pos_colon = sass.find_first_of(":", pos_colon);
585
+ // it is a selector if there was no colon found
586
+ converter.selector = pos_colon == std::string::npos;
587
+ }
588
+ }
589
+ }
590
+ }
591
+
592
+ // check if we have a BEM property (one colon and no selector)
593
+ if (sass.substr(pos_left, 1) == ":" && converter.selector == true) {
594
+ size_t pos_wspace = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left);
595
+ sass = indent + sass.substr(pos_left + 1, pos_wspace) + ":";
596
+ }
597
+
598
+ }
599
+
600
+ // terminate some statements immediately
601
+ else if (
602
+ sass.substr(pos_left, 5) == "@warn" ||
603
+ sass.substr(pos_left, 6) == "@debug" ||
604
+ sass.substr(pos_left, 6) == "@error" ||
605
+ sass.substr(pos_left, 6) == "@value" ||
606
+ sass.substr(pos_left, 8) == "@charset" ||
607
+ sass.substr(pos_left, 10) == "@namespace"
608
+ ) { sass = indent + sass.substr(pos_left); }
609
+ // replace some specific sass shorthand directives (if not fallowed by a white space character)
610
+ else if (sass.substr(pos_left, 1) == "=")
611
+ { sass = indent + "@mixin " + sass.substr(pos_left + 1); }
612
+ else if (sass.substr(pos_left, 1) == "+")
613
+ {
614
+ // must be followed by a mixin call (no whitespace afterwards or at ending directly)
615
+ if (sass[pos_left+1] != 0 && sass[pos_left+1] != ' ' && sass[pos_left+1] != '\t') {
616
+ sass = indent + "@include " + sass.substr(pos_left + 1);
617
+ }
618
+ }
619
+
620
+ // add quotes for import if needed
621
+ else if (sass.substr(pos_left, 7) == "@import")
622
+ {
623
+ // get positions for the actual import url
624
+ size_t pos_import = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left + 7);
625
+ size_t pos = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_import);
626
+ size_t start = pos;
627
+ bool in_dqstr = false;
628
+ bool in_sqstr = false;
629
+ bool is_escaped = false;
630
+ do {
631
+ if (is_escaped) {
632
+ is_escaped = false;
633
+ }
634
+ else if (sass[pos] == '\\') {
635
+ is_escaped = true;
636
+ }
637
+ else if (sass[pos] == '"') {
638
+ if (!in_sqstr) in_dqstr = ! in_dqstr;
639
+ }
640
+ else if (sass[pos] == '\'') {
641
+ if (!in_dqstr) in_sqstr = ! in_sqstr;
642
+ }
643
+ else if (in_dqstr || in_sqstr) {
644
+ // skip over quoted stuff
645
+ }
646
+ else if (sass[pos] == ',' || sass[pos] == 0) {
647
+ if (sass[start] != '"' && sass[start] != '\'' && !isUrl(sass, start)) {
648
+ size_t end = findLastCharacter(sass, pos - 1) + 1;
649
+ sass = sass.replace(end, 0, "\"");
650
+ sass = sass.replace(start, 0, "\"");
651
+ pos += 2;
652
+ }
653
+ start = findFirstCharacter(sass, pos + 1);
654
+ }
655
+ }
656
+ while (sass[pos++] != 0);
657
+
658
+ }
659
+ else if (
660
+ sass.substr(pos_left, 7) != "@return" &&
661
+ sass.substr(pos_left, 7) != "@extend" &&
662
+ sass.substr(pos_left, 8) != "@include" &&
663
+ sass.substr(pos_left, 8) != "@content"
664
+ ) {
665
+
666
+ // probably a selector anyway
667
+ converter.selector = true;
668
+ // try to find first colon in the current line
669
+ size_t pos_colon = sass.find_first_of(":", pos_left);
670
+ // assertion that we have a colon
671
+ if (pos_colon != std::string::npos)
672
+ {
673
+ // it is not a selector if we have a space after a colon
674
+ if (sass[pos_colon+1] == ' ') converter.selector = false;
675
+ if (sass[pos_colon+1] == ' ') converter.selector = false;
676
+ }
677
+
678
+ }
679
+
680
+ // current line has more indentation
681
+ if (indent.length() >= INDENT(converter).length())
682
+ {
683
+ // not in comment mode
684
+ if (IS_PARSING(converter))
685
+ {
686
+ // has meaningful chars
687
+ if (hasCharData(sass))
688
+ {
689
+ // is probably a property
690
+ // also true for selectors
691
+ converter.property = true;
692
+ }
693
+ }
694
+ }
695
+ // current line has more indentation
696
+ if (indent.length() > INDENT(converter).length())
697
+ {
698
+ // not in comment mode
699
+ if (IS_PARSING(converter))
700
+ {
701
+ // had meaningful chars
702
+ if (converter.property)
703
+ {
704
+ // print block opener
705
+ scss += opener(converter);
706
+ // push new stack context
707
+ converter.indents.push("");
708
+ // store block indentation
709
+ INDENT(converter) = indent;
710
+ }
711
+ }
712
+ // is and will be a src comment
713
+ else if (!IS_CSS_COMMENT(converter))
714
+ {
715
+ // scss does not allow multiline src comments
716
+ // therefore add forward slashes to all lines
717
+ sass.at(INDENT(converter).length()+0) = '/';
718
+ // there is an edge case here if indentation
719
+ // is minimal (will overwrite the fist char)
720
+ sass.at(INDENT(converter).length()+1) = '/';
721
+ // could code around that, but I dont' think
722
+ // this will ever be the cause for any trouble
723
+ }
724
+ }
725
+
726
+ // line is opening a new comment
727
+ if (open == "/*" || open == "//")
728
+ {
729
+ // reset the property state
730
+ converter.property = false;
731
+ // close previous comment
732
+ if (IS_CSS_COMMENT(converter) && open != "")
733
+ {
734
+ if (!STRIP_COMMENT(converter) && !CONVERT_COMMENT(converter)) scss += " */";
735
+ }
736
+ // force single line comments
737
+ // into a correct css comment
738
+ if (CONVERT_COMMENT(converter))
739
+ {
740
+ if (IS_PARSING(converter))
741
+ { sass.at(pos_left + 1) = '*'; }
742
+ }
743
+ // set comment flag
744
+ converter.comment = open;
745
+
746
+ }
747
+
748
+ // flush data only under certain conditions
749
+ if (!(
750
+ // strip css and src comments if option is set
751
+ (IS_COMMENT(converter) && STRIP_COMMENT(converter)) ||
752
+ // strip src comment even if strip option is not set
753
+ // but only if the keep src comment option is not set
754
+ (IS_SRC_COMMENT(converter) && ! KEEP_COMMENT(converter))
755
+ ))
756
+ {
757
+ // flush data and buffer whitespace
758
+ scss += flush(sass, converter);
759
+ }
760
+
761
+ // get position of last meaningful char
762
+ size_t pos_right = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE);
763
+
764
+ // check for invalid result
765
+ if (pos_right != std::string::npos)
766
+ {
767
+
768
+ // get the last meaningful char
769
+ std::string close = sass.substr(pos_right, 1);
770
+
771
+ // check if next line should be concatenated (list mode)
772
+ converter.comma = IS_PARSING(converter) && close == ",";
773
+ converter.semicolon = IS_PARSING(converter) && close == ";";
774
+
775
+ // check if we have more than
776
+ // one meaningful char
777
+ if (pos_right > 0)
778
+ {
779
+
780
+ // get the last two chars from string
781
+ std::string close = sass.substr(pos_right - 1, 2);
782
+ // update parser status for expicitly closed comment
783
+ if (close == "*/") converter.comment = "";
784
+
785
+ }
786
+
787
+ }
788
+ // EO have meaningful chars from end
789
+
790
+ }
791
+ // EO have meaningful chars from start
792
+
793
+ // return scss
794
+ return scss;
795
+
796
+ }
797
+ // EO process
798
+
799
+ // read line with either CR, LF or CR LF format
800
+ // http://stackoverflow.com/a/6089413/1550314
801
+ static std::istream& safeGetline(std::istream& is, std::string& t)
802
+ {
803
+ t.clear();
804
+
805
+ // The characters in the stream are read one-by-one using a std::streambuf.
806
+ // That is faster than reading them one-by-one using the std::istream.
807
+ // Code that uses streambuf this way must be guarded by a sentry object.
808
+ // The sentry object performs various tasks,
809
+ // such as thread synchronization and updating the stream state.
810
+
811
+ std::istream::sentry se(is, true);
812
+ std::streambuf* sb = is.rdbuf();
813
+
814
+ for(;;) {
815
+ int c = sb->sbumpc();
816
+ switch (c) {
817
+ case '\n':
818
+ return is;
819
+ case '\r':
820
+ if(sb->sgetc() == '\n')
821
+ sb->sbumpc();
822
+ return is;
823
+ case EOF:
824
+ // Also handle the case when the last line has no line ending
825
+ if(t.empty())
826
+ is.setstate(std::ios::eofbit);
827
+ return is;
828
+ default:
829
+ t += (char)c;
830
+ }
831
+ }
832
+ }
833
+
834
+ // the main converter function for c++
835
+ char* sass2scss (const std::string& sass, const int options)
836
+ {
837
+
838
+ // local variables
839
+ std::string line;
840
+ std::string scss = "";
841
+ std::stringstream stream(sass);
842
+
843
+ // create converter variable
844
+ converter converter;
845
+ // initialise all options
846
+ converter.comma = false;
847
+ converter.property = false;
848
+ converter.selector = false;
849
+ converter.semicolon = false;
850
+ converter.end_of_file = false;
851
+ converter.comment = "";
852
+ converter.whitespace = "";
853
+ converter.indents.push("");
854
+ converter.options = options;
855
+
856
+ // read line by line and process them
857
+ while(safeGetline(stream, line) && !stream.eof())
858
+ { scss += process(line, converter); }
859
+
860
+ // create mutable string
861
+ std::string closer = "";
862
+ // set the end of file flag
863
+ converter.end_of_file = true;
864
+ // process to close all open blocks
865
+ scss += process(closer, converter);
866
+
867
+ // allocate new memory on the heap
868
+ // caller has to free it after use
869
+ char * cstr = (char*) malloc (scss.length() + 1);
870
+ // create a copy of the string
871
+ strcpy (cstr, scss.c_str());
872
+ // return pointer
873
+ return &cstr[0];
874
+
875
+ }
876
+ // EO sass2scss
877
+
878
+ }
879
+ // EO namespace
880
+
881
+ // implement for c
882
+ extern "C"
883
+ {
884
+
885
+ char* ADDCALL sass2scss (const char* sass, const int options)
886
+ {
887
+ return Sass::sass2scss(sass, options);
888
+ }
889
+
890
+ // Get compiled sass2scss version
891
+ const char* ADDCALL sass2scss_version(void) {
892
+ return SASS2SCSS_VERSION;
893
+ }
894
+
895
+ }