sassc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }