sassc 0.0.1

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 (151) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +9 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +24 -0
  8. data/Rakefile +21 -0
  9. data/ext/libsass/.editorconfig +15 -0
  10. data/ext/libsass/.gitattributes +2 -0
  11. data/ext/libsass/.gitignore +61 -0
  12. data/ext/libsass/.travis.yml +38 -0
  13. data/ext/libsass/COPYING +25 -0
  14. data/ext/libsass/INSTALL +1 -0
  15. data/ext/libsass/LICENSE +25 -0
  16. data/ext/libsass/Makefile +223 -0
  17. data/ext/libsass/Makefile.am +145 -0
  18. data/ext/libsass/Readme.md +93 -0
  19. data/ext/libsass/appveyor.yml +76 -0
  20. data/ext/libsass/ast.cpp +581 -0
  21. data/ext/libsass/ast.hpp +1949 -0
  22. data/ext/libsass/ast_def_macros.hpp +16 -0
  23. data/ext/libsass/ast_factory.hpp +87 -0
  24. data/ext/libsass/ast_fwd_decl.hpp +72 -0
  25. data/ext/libsass/b64/cencode.h +32 -0
  26. data/ext/libsass/b64/encode.h +77 -0
  27. data/ext/libsass/backtrace.hpp +81 -0
  28. data/ext/libsass/base64vlq.cpp +43 -0
  29. data/ext/libsass/base64vlq.hpp +28 -0
  30. data/ext/libsass/bind.cpp +187 -0
  31. data/ext/libsass/bind.hpp +18 -0
  32. data/ext/libsass/cencode.c +102 -0
  33. data/ext/libsass/color_names.hpp +324 -0
  34. data/ext/libsass/configure.ac +130 -0
  35. data/ext/libsass/constants.cpp +144 -0
  36. data/ext/libsass/constants.hpp +145 -0
  37. data/ext/libsass/context.cpp +507 -0
  38. data/ext/libsass/context.hpp +150 -0
  39. data/ext/libsass/contextualize.cpp +157 -0
  40. data/ext/libsass/contextualize.hpp +65 -0
  41. data/ext/libsass/copy_c_str.cpp +13 -0
  42. data/ext/libsass/copy_c_str.hpp +5 -0
  43. data/ext/libsass/debug.hpp +39 -0
  44. data/ext/libsass/environment.hpp +75 -0
  45. data/ext/libsass/error_handling.cpp +28 -0
  46. data/ext/libsass/error_handling.hpp +28 -0
  47. data/ext/libsass/eval.cpp +1149 -0
  48. data/ext/libsass/eval.hpp +80 -0
  49. data/ext/libsass/expand.cpp +430 -0
  50. data/ext/libsass/expand.hpp +77 -0
  51. data/ext/libsass/extconf.rb +6 -0
  52. data/ext/libsass/extend.cpp +1962 -0
  53. data/ext/libsass/extend.hpp +50 -0
  54. data/ext/libsass/file.cpp +291 -0
  55. data/ext/libsass/file.hpp +18 -0
  56. data/ext/libsass/functions.cpp +1565 -0
  57. data/ext/libsass/functions.hpp +187 -0
  58. data/ext/libsass/inspect.cpp +727 -0
  59. data/ext/libsass/inspect.hpp +108 -0
  60. data/ext/libsass/json.cpp +1411 -0
  61. data/ext/libsass/json.hpp +117 -0
  62. data/ext/libsass/kwd_arg_macros.hpp +23 -0
  63. data/ext/libsass/m4/.gitkeep +0 -0
  64. data/ext/libsass/mapping.hpp +17 -0
  65. data/ext/libsass/memory_manager.hpp +54 -0
  66. data/ext/libsass/node.cpp +251 -0
  67. data/ext/libsass/node.hpp +122 -0
  68. data/ext/libsass/operation.hpp +153 -0
  69. data/ext/libsass/output_compressed.cpp +401 -0
  70. data/ext/libsass/output_compressed.hpp +95 -0
  71. data/ext/libsass/output_nested.cpp +364 -0
  72. data/ext/libsass/output_nested.hpp +108 -0
  73. data/ext/libsass/parser.cpp +2016 -0
  74. data/ext/libsass/parser.hpp +264 -0
  75. data/ext/libsass/paths.hpp +69 -0
  76. data/ext/libsass/position.hpp +22 -0
  77. data/ext/libsass/posix/getopt.c +562 -0
  78. data/ext/libsass/posix/getopt.h +95 -0
  79. data/ext/libsass/prelexer.cpp +688 -0
  80. data/ext/libsass/prelexer.hpp +513 -0
  81. data/ext/libsass/remove_placeholders.cpp +59 -0
  82. data/ext/libsass/remove_placeholders.hpp +43 -0
  83. data/ext/libsass/res/resource.rc +35 -0
  84. data/ext/libsass/sass.cpp +33 -0
  85. data/ext/libsass/sass.h +60 -0
  86. data/ext/libsass/sass2scss.cpp +834 -0
  87. data/ext/libsass/sass2scss.h +110 -0
  88. data/ext/libsass/sass_context.cpp +709 -0
  89. data/ext/libsass/sass_context.h +120 -0
  90. data/ext/libsass/sass_functions.cpp +137 -0
  91. data/ext/libsass/sass_functions.h +90 -0
  92. data/ext/libsass/sass_interface.cpp +277 -0
  93. data/ext/libsass/sass_interface.h +97 -0
  94. data/ext/libsass/sass_util.cpp +136 -0
  95. data/ext/libsass/sass_util.hpp +259 -0
  96. data/ext/libsass/sass_values.cpp +337 -0
  97. data/ext/libsass/sass_values.h +124 -0
  98. data/ext/libsass/script/bootstrap +10 -0
  99. data/ext/libsass/script/branding +10 -0
  100. data/ext/libsass/script/ci-build-libsass +72 -0
  101. data/ext/libsass/script/ci-install-compiler +4 -0
  102. data/ext/libsass/script/ci-install-deps +19 -0
  103. data/ext/libsass/script/ci-report-coverage +25 -0
  104. data/ext/libsass/script/coveralls-debug +32 -0
  105. data/ext/libsass/script/spec +5 -0
  106. data/ext/libsass/script/tap-driver +652 -0
  107. data/ext/libsass/script/tap-runner +1 -0
  108. data/ext/libsass/source_map.cpp +133 -0
  109. data/ext/libsass/source_map.hpp +46 -0
  110. data/ext/libsass/subset_map.hpp +145 -0
  111. data/ext/libsass/support/libsass.pc.in +11 -0
  112. data/ext/libsass/test-driver +127 -0
  113. data/ext/libsass/test/test_node.cpp +98 -0
  114. data/ext/libsass/test/test_paths.cpp +29 -0
  115. data/ext/libsass/test/test_selector_difference.cpp +28 -0
  116. data/ext/libsass/test/test_specificity.cpp +28 -0
  117. data/ext/libsass/test/test_subset_map.cpp +472 -0
  118. data/ext/libsass/test/test_superselector.cpp +71 -0
  119. data/ext/libsass/test/test_unification.cpp +33 -0
  120. data/ext/libsass/to_c.cpp +61 -0
  121. data/ext/libsass/to_c.hpp +44 -0
  122. data/ext/libsass/to_string.cpp +29 -0
  123. data/ext/libsass/to_string.hpp +32 -0
  124. data/ext/libsass/token.hpp +32 -0
  125. data/ext/libsass/units.cpp +54 -0
  126. data/ext/libsass/units.hpp +10 -0
  127. data/ext/libsass/utf8.h +34 -0
  128. data/ext/libsass/utf8/checked.h +327 -0
  129. data/ext/libsass/utf8/core.h +329 -0
  130. data/ext/libsass/utf8/unchecked.h +228 -0
  131. data/ext/libsass/utf8_string.cpp +102 -0
  132. data/ext/libsass/utf8_string.hpp +36 -0
  133. data/ext/libsass/util.cpp +189 -0
  134. data/ext/libsass/util.hpp +26 -0
  135. data/ext/libsass/win/libsass.filters +291 -0
  136. data/ext/libsass/win/libsass.sln +28 -0
  137. data/ext/libsass/win/libsass.vcxproj +255 -0
  138. data/lib/sassc.rb +6 -0
  139. data/lib/sassc/engine.rb +13 -0
  140. data/lib/sassc/native.rb +44 -0
  141. data/lib/sassc/native/native_context_api.rb +140 -0
  142. data/lib/sassc/native/native_functions_api.rb +41 -0
  143. data/lib/sassc/native/sass_input_style.rb +11 -0
  144. data/lib/sassc/native/sass_output_style.rb +10 -0
  145. data/lib/sassc/native/sass_value.rb +95 -0
  146. data/lib/sassc/native/string_list.rb +8 -0
  147. data/lib/sassc/version.rb +3 -0
  148. data/sassc.gemspec +43 -0
  149. data/test/smoke_test.rb +171 -0
  150. data/test/test_helper.rb +4 -0
  151. metadata +281 -0
@@ -0,0 +1,59 @@
1
+ #include "remove_placeholders.hpp"
2
+ #include "context.hpp"
3
+ #include "inspect.hpp"
4
+ #include "to_string.hpp"
5
+ #include <iostream>
6
+
7
+ namespace Sass {
8
+
9
+ Remove_Placeholders::Remove_Placeholders(Context& ctx)
10
+ : ctx(ctx)
11
+ { }
12
+
13
+ template<typename T>
14
+ void Remove_Placeholders::clean_selector_list(T r) {
15
+
16
+ // Create a new selector group without placeholders
17
+ Selector_List* sl = static_cast<Selector_List*>(r->selector());
18
+
19
+ if (sl) {
20
+ Selector_List* new_sl = new (ctx.mem) Selector_List(sl->path(), sl->position());
21
+
22
+ for (size_t i = 0, L = sl->length(); i < L; ++i) {
23
+ if (!(*sl)[i]->has_placeholder()) {
24
+ *new_sl << (*sl)[i];
25
+ }
26
+ }
27
+
28
+ // Set the new placeholder selector list
29
+ r->selector(new_sl);
30
+ }
31
+
32
+ // Iterate into child blocks
33
+ Block* b = r->block();
34
+
35
+ for (size_t i = 0, L = b->length(); i < L; ++i) {
36
+ Statement* stm = (*b)[i];
37
+ stm->perform(this);
38
+ }
39
+ }
40
+
41
+ void Remove_Placeholders::operator()(Block* b) {
42
+ for (size_t i = 0, L = b->length(); i < L; ++i) {
43
+ (*b)[i]->perform(this);
44
+ }
45
+ }
46
+
47
+ void Remove_Placeholders::operator()(Ruleset* r) {
48
+ clean_selector_list(r);
49
+ }
50
+
51
+ void Remove_Placeholders::operator()(Media_Block* m) {
52
+ clean_selector_list(m);
53
+ }
54
+
55
+ void Remove_Placeholders::operator()(At_Rule* a) {
56
+ if (a->block()) a->block()->perform(this);
57
+ }
58
+
59
+ }
@@ -0,0 +1,43 @@
1
+ #pragma once
2
+
3
+ #include <iostream>
4
+
5
+ #ifndef SASS_AST
6
+ #include "ast.hpp"
7
+ #endif
8
+
9
+ #ifndef SASS_OPERATION
10
+ #include "operation.hpp"
11
+ #endif
12
+
13
+ namespace Sass {
14
+
15
+ using namespace std;
16
+
17
+ struct Context;
18
+
19
+ class Remove_Placeholders : public Operation_CRTP<void, Remove_Placeholders> {
20
+
21
+ Context& ctx;
22
+
23
+ void fallback_impl(AST_Node* n) {};
24
+
25
+ public:
26
+ Remove_Placeholders(Context&);
27
+ virtual ~Remove_Placeholders() { }
28
+
29
+ using Operation<void>::operator();
30
+
31
+ void operator()(Block*);
32
+ void operator()(Ruleset*);
33
+ void operator()(Media_Block*);
34
+ void operator()(At_Rule*);
35
+
36
+ template <typename T>
37
+ void clean_selector_list(T r);
38
+
39
+ template <typename U>
40
+ void fallback(U x) { return fallback_impl(x); }
41
+ };
42
+
43
+ }
@@ -0,0 +1,35 @@
1
+ #include <windows.h>
2
+
3
+ // DLL version information.
4
+ VS_VERSION_INFO VERSIONINFO
5
+ FILEVERSION 1,0,0,0
6
+ PRODUCTVERSION 1,0,0,0
7
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
8
+ #ifdef _DEBUG
9
+ FILEFLAGS VS_FF_DEBUG | VS_FF_PRERELEASE
10
+ #else
11
+ FILEFLAGS 0
12
+ #endif
13
+ FILEOS VOS_NT_WINDOWS32
14
+ FILETYPE VFT_DLL
15
+ FILESUBTYPE VFT2_UNKNOWN
16
+ BEGIN
17
+ BLOCK "StringFileInfo"
18
+ BEGIN
19
+ BLOCK "080904b0"
20
+ BEGIN
21
+ VALUE "CompanyName", "Libsass Organization"
22
+ VALUE "FileDescription", "A C/C++ implementation of a Sass compiler"
23
+ VALUE "FileVersion", "0.9.0.0"
24
+ VALUE "InternalName", "libsass"
25
+ VALUE "LegalCopyright", "©2014 libsass.org"
26
+ VALUE "OriginalFilename", "libsass.dll"
27
+ VALUE "ProductName", "Libsass Library"
28
+ VALUE "ProductVersion", "0.9.0.0"
29
+ END
30
+ END
31
+ BLOCK "VarFileInfo"
32
+ BEGIN
33
+ VALUE "Translation", 0x809, 1200
34
+ END
35
+ END
@@ -0,0 +1,33 @@
1
+ #include <cstdlib>
2
+ #include <cstring>
3
+ #include <vector>
4
+ #include <sstream>
5
+
6
+ #include "sass.h"
7
+ #include "inspect.hpp"
8
+
9
+ extern "C" {
10
+ using namespace std;
11
+
12
+ // caller must free the returned memory
13
+ char* ADDCALL sass_string_quote (const char *str, const char quotemark) {
14
+ string quoted = Sass::quote(str, quotemark);
15
+ char *cstr = (char*) malloc(quoted.length() + 1);
16
+ std::strcpy(cstr, quoted.c_str());
17
+ return cstr;
18
+ }
19
+
20
+ // caller must free the returned memory
21
+ char* ADDCALL sass_string_unquote (const char *str) {
22
+ string unquoted = Sass::unquote(str);
23
+ char *cstr = (char*) malloc(unquoted.length() + 1);
24
+ std::strcpy(cstr, unquoted.c_str());
25
+ return cstr;
26
+ }
27
+
28
+ // Get compiled libsass version
29
+ const char* ADDCALL libsass_version(void) {
30
+ return LIBSASS_VERSION;
31
+ }
32
+
33
+ }
@@ -0,0 +1,60 @@
1
+ #ifndef SASS
2
+ #define SASS
3
+
4
+ #include <stddef.h>
5
+ #include <stdbool.h>
6
+
7
+ #ifdef _WIN32
8
+
9
+ /* You should define ADD_EXPORTS *only* when building the DLL. */
10
+ #ifdef ADD_EXPORTS
11
+ #define ADDAPI __declspec(dllexport)
12
+ #define ADDCALL __cdecl
13
+ #else
14
+ #define ADDAPI
15
+ #define ADDCALL
16
+ #endif
17
+
18
+ #else /* _WIN32 not defined. */
19
+
20
+ /* Define with no value on non-Windows OSes. */
21
+ #define ADDAPI
22
+ #define ADDCALL
23
+
24
+ #endif
25
+
26
+ #ifndef LIBSASS_VERSION
27
+ #define LIBSASS_VERSION "[NA]"
28
+ #endif
29
+
30
+ // include API headers
31
+ #include "sass_values.h"
32
+ #include "sass_functions.h"
33
+
34
+ /* Make sure functions are exported with C linkage under C++ compilers. */
35
+ #ifdef __cplusplus
36
+ extern "C" {
37
+ #endif
38
+
39
+
40
+ // Different render styles
41
+ enum Sass_Output_Style {
42
+ SASS_STYLE_NESTED,
43
+ SASS_STYLE_EXPANDED,
44
+ SASS_STYLE_COMPACT,
45
+ SASS_STYLE_COMPRESSED
46
+ };
47
+
48
+ // Some convenient string helper function
49
+ ADDAPI char* ADDCALL sass_string_quote (const char *str, const char quotemark);
50
+ ADDAPI char* ADDCALL sass_string_unquote (const char *str);
51
+
52
+ // Get compiled libsass version
53
+ ADDAPI const char* ADDCALL libsass_version(void);
54
+
55
+
56
+ #ifdef __cplusplus
57
+ } // __cplusplus defined.
58
+ #endif
59
+
60
+ #endif
@@ -0,0 +1,834 @@
1
+ // include library
2
+ #include <stack>
3
+ #include <string>
4
+ #include <cstring>
5
+ #include <cstdlib>
6
+ #include <sstream>
7
+ #include <iostream>
8
+ #include <stdio.h>
9
+
10
+ ///*
11
+ //
12
+ // src comments: comments in sass syntax (staring with //)
13
+ // css comments: multiline comments in css syntax (starting with /*)
14
+ //
15
+ // KEEP_COMMENT: keep src comments in the resulting css code
16
+ // STRIP_COMMENT: strip out all comments (either src or css)
17
+ // CONVERT_COMMENT: convert all src comments to css comments
18
+ //
19
+ //*/
20
+
21
+ // our own header
22
+ #include "sass2scss.h"
23
+
24
+ // using std::string
25
+ using namespace std;
26
+
27
+ // add namespace for c++
28
+ namespace Sass
29
+ {
30
+
31
+ // return the actual prettify value from options
32
+ #define PRETTIFY(converter) (converter.options - (converter.options & 248))
33
+ // query the options integer to check if the option is enables
34
+ #define KEEP_COMMENT(converter) ((converter.options & SASS2SCSS_KEEP_COMMENT) == SASS2SCSS_KEEP_COMMENT)
35
+ #define STRIP_COMMENT(converter) ((converter.options & SASS2SCSS_STRIP_COMMENT) == SASS2SCSS_STRIP_COMMENT)
36
+ #define CONVERT_COMMENT(converter) ((converter.options & SASS2SCSS_CONVERT_COMMENT) == SASS2SCSS_CONVERT_COMMENT)
37
+
38
+ // some makros to access the indentation stack
39
+ #define INDENT(converter) (converter.indents.top())
40
+
41
+ // some makros to query comment parser status
42
+ #define IS_PARSING(converter) (converter.comment == "")
43
+ #define IS_COMMENT(converter) (converter.comment != "")
44
+ #define IS_SRC_COMMENT(converter) (converter.comment == "//" && ! CONVERT_COMMENT(converter))
45
+ #define IS_CSS_COMMENT(converter) (converter.comment == "/*" || (converter.comment == "//" && CONVERT_COMMENT(converter)))
46
+
47
+ // pretty printer helper function
48
+ static string closer (const converter& converter)
49
+ {
50
+ return PRETTIFY(converter) == 0 ? " }" :
51
+ PRETTIFY(converter) <= 1 ? " }" :
52
+ "\n" + INDENT(converter) + "}";
53
+ }
54
+
55
+ // pretty printer helper function
56
+ static string opener (const converter& converter)
57
+ {
58
+ return PRETTIFY(converter) == 0 ? " { " :
59
+ PRETTIFY(converter) <= 2 ? " {" :
60
+ "\n" + INDENT(converter) + "{";
61
+ }
62
+
63
+ // check if the given string is a pseudo selector
64
+ // needed to differentiate from sass property syntax
65
+ static bool isPseudoSelector (string& sel)
66
+ {
67
+
68
+ size_t len = sel.length();
69
+ if (len < 1) return false;
70
+ size_t pos = sel.find_first_not_of("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1);
71
+ if (pos != string::npos) sel.erase(pos, string::npos);
72
+ size_t i = sel.length();
73
+ while (i -- > 0) { sel.at(i) = tolower(sel.at(i)); }
74
+
75
+ // CSS Level 1 - Recommendation
76
+ if (sel == ":link") return true;
77
+ if (sel == ":visited") return true;
78
+ if (sel == ":active") return true;
79
+
80
+ // CSS Level 2 (Revision 1) - Recommendation
81
+ if (sel == ":lang") return true;
82
+ if (sel == ":first-child") return true;
83
+ if (sel == ":hover") return true;
84
+ if (sel == ":focus") return true;
85
+ // disabled - also valid properties
86
+ // if (sel == ":left") return true;
87
+ // if (sel == ":right") return true;
88
+ if (sel == ":first") return true;
89
+
90
+ // Selectors Level 3 - Recommendation
91
+ if (sel == ":target") return true;
92
+ if (sel == ":root") return true;
93
+ if (sel == ":nth-child") return true;
94
+ if (sel == ":nth-last-of-child") return true;
95
+ if (sel == ":nth-of-type") return true;
96
+ if (sel == ":nth-last-of-type") return true;
97
+ if (sel == ":last-child") return true;
98
+ if (sel == ":first-of-type") return true;
99
+ if (sel == ":last-of-type") return true;
100
+ if (sel == ":only-child") return true;
101
+ if (sel == ":only-of-type") return true;
102
+ if (sel == ":empty") return true;
103
+ if (sel == ":not") return true;
104
+
105
+ // CSS Basic User Interface Module Level 3 - Working Draft
106
+ if (sel == ":default") return true;
107
+ if (sel == ":valid") return true;
108
+ if (sel == ":invalid") return true;
109
+ if (sel == ":in-range") return true;
110
+ if (sel == ":out-of-range") return true;
111
+ if (sel == ":required") return true;
112
+ if (sel == ":optional") return true;
113
+ if (sel == ":read-only") return true;
114
+ if (sel == ":read-write") return true;
115
+ if (sel == ":dir") return true;
116
+ if (sel == ":enabled") return true;
117
+ if (sel == ":disabled") return true;
118
+ if (sel == ":checked") return true;
119
+ if (sel == ":indeterminate") return true;
120
+ if (sel == ":nth-last-child") return true;
121
+
122
+ // Selectors Level 4 - Working Draft
123
+ if (sel == ":any-link") return true;
124
+ if (sel == ":local-link") return true;
125
+ if (sel == ":scope") return true;
126
+ if (sel == ":active-drop-target") return true;
127
+ if (sel == ":valid-drop-target") return true;
128
+ if (sel == ":invalid-drop-target") return true;
129
+ if (sel == ":current") return true;
130
+ if (sel == ":past") return true;
131
+ if (sel == ":future") return true;
132
+ if (sel == ":placeholder-shown") return true;
133
+ if (sel == ":user-error") return true;
134
+ if (sel == ":blank") return true;
135
+ if (sel == ":nth-match") return true;
136
+ if (sel == ":nth-last-match") return true;
137
+ if (sel == ":nth-column") return true;
138
+ if (sel == ":nth-last-column") return true;
139
+ if (sel == ":matches") return true;
140
+
141
+ // Fullscreen API - Living Standard
142
+ if (sel == ":fullscreen") return true;
143
+
144
+ // not a pseudo selector
145
+ return false;
146
+
147
+ }
148
+
149
+ // check if there is some char data
150
+ // will ignore everything in comments
151
+ static bool hasCharData (string& sass)
152
+ {
153
+
154
+ size_t col_pos = 0;
155
+
156
+ while (true)
157
+ {
158
+
159
+ // try to find some meaningfull char
160
+ col_pos = sass.find_first_not_of(" \t\n\v\f\r", col_pos);
161
+
162
+ // there was no meaningfull char found
163
+ if (col_pos == string::npos) return false;
164
+
165
+ // found a multiline comment opener
166
+ if (sass.substr(col_pos, 2) == "/*")
167
+ {
168
+ // find the multiline comment closer
169
+ col_pos = sass.find("*/", col_pos);
170
+ // maybe we did not find the closer here
171
+ if (col_pos == string::npos) return false;
172
+ // skip closer
173
+ col_pos += 2;
174
+ }
175
+ else
176
+ {
177
+ return true;
178
+ }
179
+
180
+ }
181
+
182
+ }
183
+ // EO hasCharData
184
+
185
+ // find src comment opener
186
+ // correctly skips quoted strings
187
+ static size_t findCommentOpener (string& sass)
188
+ {
189
+
190
+ size_t col_pos = 0;
191
+ bool apoed = false;
192
+ bool quoted = false;
193
+ bool comment = false;
194
+ size_t brackets = 0;
195
+
196
+ while (col_pos != string::npos)
197
+ {
198
+
199
+ // process all interesting chars
200
+ col_pos = sass.find_first_of("\"\'/\\*()", col_pos);
201
+
202
+ // assertion for valid result
203
+ if (col_pos != string::npos)
204
+ {
205
+ char character = sass.at(col_pos);
206
+
207
+ if (character == '(')
208
+ {
209
+ if (!quoted && !apoed) brackets ++;
210
+ }
211
+ else if (character == ')')
212
+ {
213
+ if (!quoted && !apoed) brackets --;
214
+ }
215
+ else if (character == '\"')
216
+ {
217
+ // invert quote bool
218
+ if (!apoed && !comment) quoted = !quoted;
219
+ }
220
+ else if (character == '\'')
221
+ {
222
+ // invert quote bool
223
+ if (!quoted && !comment) apoed = !apoed;
224
+ }
225
+ else if (col_pos > 0 && character == '/')
226
+ {
227
+ if (sass.at(col_pos - 1) == '*')
228
+ {
229
+ comment = false;
230
+ }
231
+ // next needs to be a slash too
232
+ else if (sass.at(col_pos - 1) == '/')
233
+ {
234
+ // only found if not in single or double quote, bracket or comment
235
+ if (!quoted && !apoed && !comment && brackets == 0) return col_pos - 1;
236
+ }
237
+ }
238
+ else if (character == '\\')
239
+ {
240
+ // skip next char if in quote
241
+ if (quoted || apoed) col_pos ++;
242
+ }
243
+ // this might be a comment opener
244
+ else if (col_pos > 0 && character == '*')
245
+ {
246
+ // opening a multiline comment
247
+ if (sass.at(col_pos - 1) == '/')
248
+ {
249
+ // we are now in a comment
250
+ if (!quoted && !apoed) comment = true;
251
+ }
252
+ }
253
+
254
+ // skip char
255
+ col_pos ++;
256
+
257
+ }
258
+
259
+ }
260
+ // EO while
261
+
262
+ return col_pos;
263
+
264
+ }
265
+ // EO findCommentOpener
266
+
267
+ // remove multiline comments from sass string
268
+ // correctly skips quoted strings
269
+ static string removeMultilineComment (string &sass)
270
+ {
271
+
272
+ string clean = "";
273
+ size_t col_pos = 0;
274
+ size_t open_pos = 0;
275
+ size_t close_pos = 0;
276
+ bool apoed = false;
277
+ bool quoted = false;
278
+ bool comment = false;
279
+
280
+ // process sass til string end
281
+ while (col_pos != string::npos)
282
+ {
283
+
284
+ // process all interesting chars
285
+ col_pos = sass.find_first_of("\"\'/\\*", col_pos);
286
+
287
+ // assertion for valid result
288
+ if (col_pos != string::npos)
289
+ {
290
+ char character = sass.at(col_pos);
291
+
292
+ // found quoted string delimiter
293
+ if (character == '\"')
294
+ {
295
+ if (!apoed && !comment) quoted = !quoted;
296
+ }
297
+ else if (character == '\'')
298
+ {
299
+ if (!quoted && !comment) apoed = !apoed;
300
+ }
301
+ // found possible comment closer
302
+ else if (character == '/')
303
+ {
304
+ // look back to see if it is actually a closer
305
+ if (comment && col_pos > 0 && sass.at(col_pos - 1) == '*')
306
+ {
307
+ close_pos = col_pos + 1; comment = false;
308
+ }
309
+ }
310
+ else if (character == '\\')
311
+ {
312
+ // skip escaped char
313
+ if (quoted || apoed) col_pos ++;
314
+ }
315
+ // this might be a comment opener
316
+ else if (character == '*')
317
+ {
318
+ // look back to see if it is actually an opener
319
+ if (!quoted && !apoed && col_pos > 0 && sass.at(col_pos - 1) == '/')
320
+ {
321
+ comment = true; open_pos = col_pos - 1;
322
+ clean += sass.substr(close_pos, open_pos - close_pos);
323
+ }
324
+ }
325
+
326
+ // skip char
327
+ col_pos ++;
328
+
329
+ }
330
+
331
+ }
332
+ // EO while
333
+
334
+ // add final parts (add half open comment text)
335
+ if (comment) clean += sass.substr(open_pos);
336
+ else clean += sass.substr(close_pos);
337
+
338
+ // return string
339
+ return clean;
340
+
341
+ }
342
+ // EO removeMultilineComment
343
+
344
+ // right trim a given string
345
+ string rtrim(const string &sass)
346
+ {
347
+ string trimmed = sass;
348
+ size_t pos_ws = trimmed.find_last_not_of(" \t\n\v\f\r");
349
+ if (pos_ws != string::npos)
350
+ { trimmed.erase(pos_ws + 1); }
351
+ else { trimmed.clear(); }
352
+ return trimmed;
353
+ }
354
+ // EO rtrim
355
+
356
+ // flush whitespace and print additional text, but
357
+ // only print additional chars and buffer whitespace
358
+ string flush (string& sass, converter& converter)
359
+ {
360
+
361
+ // return flushed
362
+ string scss = "";
363
+
364
+ // print whitespace buffer
365
+ scss += PRETTIFY(converter) > 0 ?
366
+ converter.whitespace : "";
367
+ // reset whitespace buffer
368
+ converter.whitespace = "";
369
+
370
+ // remove possible newlines from string
371
+ size_t pos_right = sass.find_last_not_of("\n\r");
372
+ if (pos_right == string::npos) return scss;
373
+
374
+ // get the linefeeds from the string
375
+ string lfs = sass.substr(pos_right + 1);
376
+ sass = sass.substr(0, pos_right + 1);
377
+
378
+ // find some source comment opener
379
+ size_t comment_pos = findCommentOpener(sass);
380
+ // check if there was a source comment
381
+ if (comment_pos != string::npos)
382
+ {
383
+ // convert comment (but only outside other coments)
384
+ if (CONVERT_COMMENT(converter) && !IS_COMMENT(converter))
385
+ {
386
+ // convert to multiline comment
387
+ sass.at(comment_pos + 1) = '*';
388
+ // add comment node to the whitespace
389
+ sass += " */";
390
+ }
391
+ // not at line start
392
+ if (comment_pos > 0)
393
+ {
394
+ // also include whitespace before the actual comment opener
395
+ size_t ws_pos = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE, comment_pos - 1);
396
+ comment_pos = ws_pos == string::npos ? 0 : ws_pos + 1;
397
+ }
398
+ if (!STRIP_COMMENT(converter))
399
+ {
400
+ // add comment node to the whitespace
401
+ converter.whitespace += sass.substr(comment_pos);
402
+ }
403
+ else
404
+ {
405
+ // sass = removeMultilineComments(sass);
406
+ }
407
+ // update the actual sass code
408
+ sass = sass.substr(0, comment_pos);
409
+ }
410
+
411
+ // add newline as getline discharged it
412
+ converter.whitespace += lfs + "\n";
413
+
414
+ // maybe remove any leading whitespace
415
+ if (PRETTIFY(converter) == 0)
416
+ {
417
+ // remove leading whitespace and update string
418
+ size_t pos_left = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE);
419
+ if (pos_left != string::npos) sass = sass.substr(pos_left);
420
+ }
421
+
422
+ // add flushed data
423
+ scss += sass;
424
+
425
+ // return string
426
+ return scss;
427
+
428
+ }
429
+ // EO flush
430
+
431
+ // process a line of the sass text
432
+ string process (string& sass, converter& converter)
433
+ {
434
+
435
+ // resulting string
436
+ string scss = "";
437
+
438
+ // strip multi line comments
439
+ if (STRIP_COMMENT(converter))
440
+ {
441
+ sass = removeMultilineComment(sass);
442
+ }
443
+
444
+ // right trim input
445
+ sass = rtrim(sass);
446
+
447
+ // get postion of first meaningfull character in string
448
+ size_t pos_left = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE);
449
+
450
+ // special case for final run
451
+ if (converter.end_of_file) pos_left = 0;
452
+
453
+ // maybe has only whitespace
454
+ if (pos_left == string::npos)
455
+ {
456
+ // just add complete whitespace
457
+ converter.whitespace += sass + "\n";
458
+ }
459
+ // have meaningfull first char
460
+ else
461
+ {
462
+
463
+ // extract and store indentation string
464
+ string indent = sass.substr(0, pos_left);
465
+
466
+ // check if current line starts a comment
467
+ string open = sass.substr(pos_left, 2);
468
+
469
+ // line has less or same indentation
470
+ // finalize previous open parser context
471
+ if (indent.length() <= INDENT(converter).length())
472
+ {
473
+
474
+ // close multilinie comment
475
+ if (IS_CSS_COMMENT(converter))
476
+ {
477
+ // check if comments will be stripped anyway
478
+ if (!STRIP_COMMENT(converter)) scss += " */";
479
+ }
480
+ // close src comment comment
481
+ else if (IS_SRC_COMMENT(converter))
482
+ {
483
+ // add a newline to avoid closer on same line
484
+ // this would put the bracket in the comment node
485
+ // no longer needed since we parse them correctly
486
+ // if (KEEP_COMMENT(converter)) scss += "\n";
487
+ }
488
+ // close css properties
489
+ else if (converter.property)
490
+ {
491
+ // add closer unless in concat mode
492
+ if (!converter.comma)
493
+ {
494
+ // if there was no colon we have a selector
495
+ // looks like there were no inner properties
496
+ if (converter.selector) scss += " {}";
497
+ // add final semicolon
498
+ else if (!converter.semicolon) scss += ";";
499
+ }
500
+ }
501
+
502
+ // reset comment state
503
+ converter.comment = "";
504
+
505
+ }
506
+
507
+ // make sure we close every "higher" block
508
+ while (indent.length() < INDENT(converter).length())
509
+ {
510
+ // pop stacked context
511
+ converter.indents.pop();
512
+ // print close bracket
513
+ if (IS_PARSING(converter))
514
+ { scss += closer(converter); }
515
+ else { scss += " */"; }
516
+ // reset comment state
517
+ converter.comment = "";
518
+ }
519
+
520
+ // reset converter state
521
+ converter.selector = false;
522
+
523
+ // check if we have sass property syntax
524
+ if (sass.substr(pos_left, 1) == ":" && sass.substr(pos_left, 2) != "::")
525
+ {
526
+
527
+ // default to a selector
528
+ // change back if property found
529
+ converter.selector = true;
530
+ // get postion of first whitespace char
531
+ size_t pos_wspace = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left);
532
+ // assertion check for valid result
533
+ if (pos_wspace != string::npos)
534
+ {
535
+ // get the possible pseudo selector
536
+ string pseudo = sass.substr(pos_left, pos_wspace - pos_left);
537
+ // get position of the first real property value char
538
+ // pseudo selectors get this far, but have no actual value
539
+ size_t pos_value = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_wspace);
540
+ // assertion check for valid result
541
+ if (pos_value != string::npos)
542
+ {
543
+ // only process if not (fallowed by a semicolon or is a pseudo selector)
544
+ if (!(sass.at(pos_value) == ':' || isPseudoSelector(pseudo)))
545
+ {
546
+ // create new string by interchanging the colon sign for property and value
547
+ sass = indent + sass.substr(pos_left + 1, pos_wspace - pos_left - 1) + ":" + sass.substr(pos_wspace);
548
+ // try to find a colon in the current line, but only ...
549
+ size_t pos_colon = sass.find_first_not_of(":", pos_left);
550
+ // assertion for valid result
551
+ if (pos_colon != string::npos)
552
+ {
553
+ // ... after the first word (skip begining colons)
554
+ pos_colon = sass.find_first_of(":", pos_colon);
555
+ // it is a selector if there was no colon found
556
+ converter.selector = pos_colon == string::npos;
557
+ }
558
+ }
559
+ }
560
+ }
561
+
562
+ }
563
+
564
+ // terminate warn and debug statements immediately
565
+ else if (
566
+ sass.substr(pos_left, 5) == "@warn" ||
567
+ sass.substr(pos_left, 6) == "@debug" ||
568
+ sass.substr(pos_left, 6) == "@error"
569
+ ) { sass = indent + sass.substr(pos_left); }
570
+ // replace some specific sass shorthand directives (if not fallowed by a white space character)
571
+ else if (sass.substr(pos_left, 1) == "=" && sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left) != pos_left + 1)
572
+ { sass = indent + "@mixin " + sass.substr(pos_left + 1); }
573
+ else if (sass.substr(pos_left, 1) == "+" && sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left) != pos_left + 1)
574
+ { sass = indent + "@include " + sass.substr(pos_left + 1); }
575
+
576
+ // add quotes for import if needed
577
+ else if (sass.substr(pos_left, 7) == "@import")
578
+ {
579
+ // get positions for the actual import url
580
+ size_t pos_import = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left + 7);
581
+ size_t pos_quote = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_import);
582
+ // leave proper urls untouched
583
+ if (sass.substr(pos_quote, 4) != "url(")
584
+ {
585
+ // check if the url appears to be already quoted
586
+ if (sass.substr(pos_quote, 1) != "\"" && sass.substr(pos_quote, 1) != "\'")
587
+ {
588
+ // get position of the last char on the line
589
+ size_t pos_end = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE);
590
+ // assertion check for valid result
591
+ if (pos_end != string::npos)
592
+ {
593
+ // add quotes around the full line after the import statement
594
+ sass = sass.substr(0, pos_quote) + "\"" + sass.substr(pos_quote, pos_end - pos_quote + 1) + "\"";
595
+ }
596
+ }
597
+ }
598
+
599
+ }
600
+ else if (
601
+ sass.substr(pos_left, 7) != "@return" &&
602
+ sass.substr(pos_left, 7) != "@extend" &&
603
+ sass.substr(pos_left, 8) != "@content"
604
+ ) {
605
+
606
+ // try to find a colon in the current line, but only ...
607
+ size_t pos_colon = sass.find_first_not_of(":", pos_left);
608
+ // assertion for valid result
609
+ if (pos_colon != string::npos)
610
+ {
611
+ // ... after the first word (skip begining colons)
612
+ pos_colon = sass.find_first_of(":", pos_colon);
613
+ // it is a selector if there was no colon found
614
+ converter.selector = pos_colon == string::npos;
615
+ }
616
+
617
+ }
618
+
619
+ // current line has more indentation
620
+ if (indent.length() >= INDENT(converter).length())
621
+ {
622
+ // not in comment mode
623
+ if (IS_PARSING(converter))
624
+ {
625
+ // has meaningfull chars
626
+ if (hasCharData(sass))
627
+ {
628
+ // is probably a property
629
+ // also true for selectors
630
+ converter.property = true;
631
+ }
632
+ }
633
+ }
634
+ // current line has more indentation
635
+ if (indent.length() > INDENT(converter).length())
636
+ {
637
+ // not in comment mode
638
+ if (IS_PARSING(converter))
639
+ {
640
+ // had meaningfull chars
641
+ if (converter.property)
642
+ {
643
+ // print block opener
644
+ scss += opener(converter);
645
+ // push new stack context
646
+ converter.indents.push("");
647
+ // store block indentation
648
+ INDENT(converter) = indent;
649
+ }
650
+ }
651
+ // is and will be a src comment
652
+ else if (!IS_CSS_COMMENT(converter))
653
+ {
654
+ // scss does not allow multiline src comments
655
+ // therefore add forward slashes to all lines
656
+ sass.at(INDENT(converter).length()+0) = '/';
657
+ // there is an edge case here if indentation
658
+ // is minimal (will overwrite the fist char)
659
+ sass.at(INDENT(converter).length()+1) = '/';
660
+ // could code around that, but I dont' think
661
+ // this will ever be the cause for any trouble
662
+ }
663
+ }
664
+
665
+ // line is opening a new comment
666
+ if (open == "/*" || open == "//")
667
+ {
668
+ // reset the property state
669
+ converter.property = false;
670
+ // close previous comment
671
+ if (IS_CSS_COMMENT(converter) && open != "")
672
+ {
673
+ if (!STRIP_COMMENT(converter) && !CONVERT_COMMENT(converter)) scss += " */";
674
+ }
675
+ // force single line comments
676
+ // into a correct css comment
677
+ if (CONVERT_COMMENT(converter))
678
+ {
679
+ if (IS_PARSING(converter))
680
+ { sass.at(pos_left + 1) = '*'; }
681
+ }
682
+ // set comment flag
683
+ converter.comment = open;
684
+
685
+ }
686
+
687
+ // flush data only under certain conditions
688
+ if (!(
689
+ // strip css and src comments if option is set
690
+ (IS_COMMENT(converter) && STRIP_COMMENT(converter)) ||
691
+ // strip src comment even if strip option is not set
692
+ // but only if the keep src comment option is not set
693
+ (IS_SRC_COMMENT(converter) && ! KEEP_COMMENT(converter))
694
+ ))
695
+ {
696
+ // flush data and buffer whitespace
697
+ scss += flush(sass, converter);
698
+ }
699
+
700
+ // get postion of last meaningfull char
701
+ size_t pos_right = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE);
702
+
703
+ // check for invalid result
704
+ if (pos_right != string::npos)
705
+ {
706
+
707
+ // get the last meaningfull char
708
+ string close = sass.substr(pos_right, 1);
709
+
710
+ // check if next line should be concatenated (list mode)
711
+ converter.comma = IS_PARSING(converter) && close == ",";
712
+ converter.semicolon = IS_PARSING(converter) && close == ";";
713
+
714
+ // check if we have more than
715
+ // one meaningfull char
716
+ if (pos_right > 0)
717
+ {
718
+
719
+ // get the last two chars from string
720
+ string close = sass.substr(pos_right - 1, 2);
721
+ // update parser status for expicitly closed comment
722
+ if (close == "*/") converter.comment = "";
723
+
724
+ }
725
+
726
+ }
727
+ // EO have meaningfull chars from end
728
+
729
+ }
730
+ // EO have meaningfull chars from start
731
+
732
+ // return scss
733
+ return scss;
734
+
735
+ }
736
+ // EO process
737
+
738
+ // read line with either CR, LF or CR LF format
739
+ // http://stackoverflow.com/a/6089413/1550314
740
+ static std::istream& safeGetline(std::istream& is, std::string& t)
741
+ {
742
+ t.clear();
743
+
744
+ // The characters in the stream are read one-by-one using a std::streambuf.
745
+ // That is faster than reading them one-by-one using the std::istream.
746
+ // Code that uses streambuf this way must be guarded by a sentry object.
747
+ // The sentry object performs various tasks,
748
+ // such as thread synchronization and updating the stream state.
749
+
750
+ std::istream::sentry se(is, true);
751
+ std::streambuf* sb = is.rdbuf();
752
+
753
+ for(;;) {
754
+ int c = sb->sbumpc();
755
+ switch (c) {
756
+ case '\n':
757
+ return is;
758
+ case '\r':
759
+ if(sb->sgetc() == '\n')
760
+ sb->sbumpc();
761
+ return is;
762
+ case EOF:
763
+ // Also handle the case when the last line has no line ending
764
+ if(t.empty())
765
+ is.setstate(std::ios::eofbit);
766
+ return is;
767
+ default:
768
+ t += (char)c;
769
+ }
770
+ }
771
+ }
772
+
773
+ // the main converter function for c++
774
+ char* sass2scss (const string sass, const int options)
775
+ {
776
+
777
+ // local variables
778
+ string line;
779
+ string scss = "";
780
+ stringstream stream(sass);
781
+
782
+ // create converter variable
783
+ converter converter;
784
+ // initialise all options
785
+ converter.comma = false;
786
+ converter.property = false;
787
+ converter.selector = false;
788
+ converter.semicolon = false;
789
+ converter.end_of_file = false;
790
+ converter.comment = "";
791
+ converter.whitespace = "";
792
+ converter.indents.push("");
793
+ converter.options = options;
794
+
795
+ // read line by line and process them
796
+ while(safeGetline(stream, line) && !stream.eof())
797
+ { scss += process(line, converter); }
798
+
799
+ // create mutable string
800
+ string closer = "";
801
+ // set the end of file flag
802
+ converter.end_of_file = true;
803
+ // process to close all open blocks
804
+ scss += process(closer, converter);
805
+
806
+ // allocate new memory on the heap
807
+ // caller has to free it after use
808
+ char * cstr = (char*) malloc (scss.length() + 1);
809
+ // create a copy of the string
810
+ strcpy (cstr, scss.c_str());
811
+ // return pointer
812
+ return &cstr[0];
813
+
814
+ }
815
+ // EO sass2scss
816
+
817
+ }
818
+ // EO namespace
819
+
820
+ // implement for c
821
+ extern "C"
822
+ {
823
+
824
+ char* ADDCALL sass2scss (const char* sass, const int options)
825
+ {
826
+ return Sass::sass2scss(sass, options);
827
+ }
828
+
829
+ // Get compiled sass2scss version
830
+ const char* ADDCALL sass2scss_version(void) {
831
+ return SASS2SCSS_VERSION;
832
+ }
833
+
834
+ }