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,50 @@
1
+ #define SASS_EXTEND
2
+
3
+ #include <vector>
4
+ #include <map>
5
+ #include <set>
6
+ #include <iostream>
7
+
8
+ #ifndef SASS_AST
9
+ #include "ast.hpp"
10
+ #endif
11
+
12
+ #ifndef SASS_OPERATION
13
+ #include "operation.hpp"
14
+ #endif
15
+
16
+ #ifndef SASS_SUBSET_MAP
17
+ #include "subset_map.hpp"
18
+ #endif
19
+
20
+ namespace Sass {
21
+ using namespace std;
22
+
23
+ struct Context;
24
+
25
+ typedef Subset_Map<string, pair<Complex_Selector*, Compound_Selector*> > ExtensionSubsetMap;
26
+
27
+ class Extend : public Operation_CRTP<void, Extend> {
28
+
29
+ Context& ctx;
30
+ ExtensionSubsetMap& subset_map;
31
+
32
+ void fallback_impl(AST_Node* n) { };
33
+
34
+ public:
35
+ Extend(Context&, ExtensionSubsetMap&);
36
+ virtual ~Extend() { }
37
+
38
+ using Operation<void>::operator();
39
+
40
+ void operator()(Block*);
41
+ void operator()(Ruleset*);
42
+ void operator()(Feature_Block*);
43
+ void operator()(Media_Block*);
44
+ void operator()(At_Rule*);
45
+
46
+ template <typename U>
47
+ void fallback(U x) { return fallback_impl(x); }
48
+ };
49
+
50
+ }
@@ -0,0 +1,291 @@
1
+ #ifdef _WIN32
2
+ #include <direct.h>
3
+ #define getcwd _getcwd
4
+ #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
5
+ #else
6
+ #include <unistd.h>
7
+ #endif
8
+
9
+ #include <iostream>
10
+ #include <fstream>
11
+ #include <cctype>
12
+ #include <algorithm>
13
+ #include <sys/stat.h>
14
+ #include "file.hpp"
15
+ #include "context.hpp"
16
+ #include "utf8_string.hpp"
17
+ #include "sass2scss.h"
18
+
19
+ #ifdef _WIN32
20
+ #include <windows.h>
21
+ #endif
22
+
23
+ #ifndef FS_CASE_SENSITIVE
24
+ #ifdef _WIN32
25
+ #define FS_CASE_SENSITIVE 0
26
+ #else
27
+ #define FS_CASE_SENSITIVE 1
28
+ #endif
29
+ #endif
30
+
31
+ namespace Sass {
32
+ namespace File {
33
+ using namespace std;
34
+
35
+ string get_cwd()
36
+ {
37
+ const size_t wd_len = 1024;
38
+ char wd[wd_len];
39
+ string cwd = getcwd(wd, wd_len);
40
+ #ifdef _WIN32
41
+ //convert backslashes to forward slashes
42
+ replace(cwd.begin(), cwd.end(), '\\', '/');
43
+ #endif
44
+ if (cwd[cwd.length() - 1] != '/') cwd += '/';
45
+ return cwd;
46
+ }
47
+
48
+ // no physical check on filesystem
49
+ // only a logical cleanup of a path
50
+ string make_canonical_path (string path)
51
+ {
52
+
53
+ // declarations
54
+ size_t pos;
55
+
56
+ #ifdef _WIN32
57
+ //convert backslashes to forward slashes
58
+ replace(path.begin(), path.end(), '\\', '/');
59
+ #endif
60
+
61
+ pos = 0; // remove all self references inside the path string
62
+ while((pos = path.find("/./", pos)) != string::npos) path.erase(pos, 2);
63
+
64
+ pos = 0; // remove all leading and trailing self references
65
+ while(path.length() > 1 && path.substr(0, 2) == "./") path.erase(0, 2);
66
+ while((pos = path.length()) > 1 && path.substr(pos - 2) == "/.") path.erase(pos - 2);
67
+
68
+ pos = 0; // collapse multiple delimiters into a single one
69
+ while((pos = path.find("//", pos)) != string::npos) path.erase(pos, 1);
70
+
71
+ return path;
72
+
73
+ }
74
+
75
+ size_t find_last_folder_separator(const string& path, size_t limit = string::npos)
76
+ {
77
+ size_t pos = string::npos;
78
+ size_t pos_p = path.find_last_of('/', limit);
79
+ #ifdef _WIN32
80
+ size_t pos_w = path.find_last_of('\\', limit);
81
+ #else
82
+ size_t pos_w = string::npos;
83
+ #endif
84
+ if (pos_p != string::npos && pos_w != string::npos) {
85
+ pos = max(pos_p, pos_w);
86
+ }
87
+ else if (pos_p != string::npos) {
88
+ pos = pos_p;
89
+ }
90
+ else {
91
+ pos = pos_w;
92
+ }
93
+ return pos;
94
+ }
95
+
96
+ string base_name(string path)
97
+ {
98
+ size_t pos = find_last_folder_separator(path);
99
+ if (pos == string::npos) return path;
100
+ else return path.substr(pos+1);
101
+ }
102
+
103
+ string dir_name(string path)
104
+ {
105
+ size_t pos = find_last_folder_separator(path);
106
+ if (pos == string::npos) return "";
107
+ else return path.substr(0, pos+1);
108
+ }
109
+
110
+ string join_paths(string l, string r)
111
+ {
112
+ if (l.empty()) return r;
113
+ if (r.empty()) return l;
114
+ if (is_absolute_path(r)) return r;
115
+
116
+ if (l[l.length()-1] != '/') l += '/';
117
+
118
+ while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) {
119
+ r = r.substr(3);
120
+ size_t pos = find_last_folder_separator(l, l.length() - 2);
121
+ l = l.substr(0, pos == string::npos ? pos : pos + 1);
122
+ }
123
+
124
+ return l + r;
125
+ }
126
+
127
+ bool is_absolute_path(const string& path)
128
+ {
129
+ if (path[0] == '/') return true;
130
+ // TODO: UN-HACKIFY THIS
131
+ #ifdef _WIN32
132
+ if (path.length() >= 2 && isalpha(path[0]) && path[1] == ':') return true;
133
+ #endif
134
+ return false;
135
+ }
136
+
137
+ string make_absolute_path(const string& path, const string& cwd)
138
+ {
139
+ return make_canonical_path((is_absolute_path(path) ? path : join_paths(cwd, path)));
140
+ }
141
+
142
+ string resolve_relative_path(const string& uri, const string& base, const string& cwd)
143
+ {
144
+
145
+ string absolute_uri = make_absolute_path(uri, cwd);
146
+ string absolute_base = make_absolute_path(base, cwd);
147
+
148
+ string stripped_uri = "";
149
+ string stripped_base = "";
150
+
151
+ size_t index = 0;
152
+ size_t minSize = min(absolute_uri.size(), absolute_base.size());
153
+ for (size_t i = 0; i < minSize; ++i) {
154
+ #ifdef FS_CASE_SENSITIVE
155
+ if (absolute_uri[i] != absolute_base[i]) break;
156
+ #else
157
+ // compare the charactes in a case insensitive manner
158
+ // windows fs is only case insensitive in ascii ranges
159
+ if (tolower(absolute_uri[i]) != tolower(absolute_base[i])) break;
160
+ #endif
161
+ if (absolute_uri[i] == '/') index = i + 1;
162
+ }
163
+ for (size_t i = index; i < absolute_uri.size(); ++i) {
164
+ stripped_uri += absolute_uri[i];
165
+ }
166
+ for (size_t i = index; i < absolute_base.size(); ++i) {
167
+ stripped_base += absolute_base[i];
168
+ }
169
+
170
+ size_t left = 0;
171
+ size_t directories = 0;
172
+ for (size_t right = 0; right < stripped_base.size(); ++right) {
173
+ if (stripped_base[right] == '/') {
174
+ if (stripped_base.substr(left, 2) != "..") {
175
+ ++directories;
176
+ }
177
+ else if (directories > 1) {
178
+ --directories;
179
+ }
180
+ else {
181
+ directories = 0;
182
+ }
183
+ left = right + 1;
184
+ }
185
+ }
186
+
187
+ string result = "";
188
+ for (size_t i = 0; i < directories; ++i) {
189
+ result += "../";
190
+ }
191
+ result += stripped_uri;
192
+
193
+ return result;
194
+ }
195
+
196
+ char* resolve_and_load(string path, string& real_path)
197
+ {
198
+ // Resolution order for ambiguous imports:
199
+ // (1) filename as given
200
+ // (2) underscore + given
201
+ // (3) underscore + given + extension
202
+ // (4) given + extension
203
+ char* contents = 0;
204
+ real_path = path;
205
+ // if the file isn't found with the given filename ...
206
+ if (!(contents = read_file(real_path))) {
207
+ string dir(dir_name(path));
208
+ string base(base_name(path));
209
+ string _base("_" + base);
210
+ real_path = dir + _base;
211
+ // if the file isn't found with '_' + filename ...
212
+ if (!(contents = read_file(real_path))) {
213
+ string _base_scss(_base + ".scss");
214
+ real_path = dir + _base_scss;
215
+ // if the file isn't found with '_' + filename + ".scss" ...
216
+ if (!(contents = read_file(real_path))) {
217
+ string _base_sass(_base + ".sass");
218
+ real_path = dir + _base_sass;
219
+ // if the file isn't found with '_' + filename + ".sass" ...
220
+ if (!(contents = read_file(real_path))) {
221
+ string base_scss(base + ".scss");
222
+ real_path = dir + base_scss;
223
+ // if the file isn't found with filename + ".scss" ...
224
+ if (!(contents = read_file(real_path))) {
225
+ string base_sass(base + ".sass");
226
+ real_path = dir + base_sass;
227
+ // if the file isn't found with filename + ".sass" ...
228
+ if (!(contents = read_file(real_path))) {
229
+ // default back to scss version
230
+ real_path = dir + base_scss;
231
+ }
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }
237
+ #ifdef _WIN32
238
+ // convert Windows backslashes to URL forward slashes
239
+ replace(real_path.begin(), real_path.end(), '\\', '/');
240
+ #endif
241
+ return contents;
242
+ }
243
+
244
+ char* read_file(string path)
245
+ {
246
+ #ifdef _WIN32
247
+ BYTE* pBuffer;
248
+ DWORD dwBytes;
249
+ // windows unicode filepaths are encoded in utf16
250
+ wstring wpath = UTF_8::convert_to_utf16(path);
251
+ HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
252
+ if (hFile == INVALID_HANDLE_VALUE) return 0;
253
+ DWORD dwFileLength = GetFileSize(hFile, NULL);
254
+ if (dwFileLength == INVALID_FILE_SIZE) return 0;
255
+ pBuffer = new BYTE[dwFileLength + 1];
256
+ ReadFile(hFile, pBuffer, dwFileLength, &dwBytes, NULL);
257
+ pBuffer[dwFileLength] = '\0';
258
+ CloseHandle(hFile);
259
+ // just convert from unsigned char*
260
+ char* contents = (char*) pBuffer;
261
+ #else
262
+ struct stat st;
263
+ if (stat(path.c_str(), &st) == -1 || S_ISDIR(st.st_mode)) return 0;
264
+ ifstream file(path.c_str(), ios::in | ios::binary | ios::ate);
265
+ char* contents = 0;
266
+ if (file.is_open()) {
267
+ size_t size = file.tellg();
268
+ contents = new char[size + 1]; // extra byte for the null char
269
+ file.seekg(0, ios::beg);
270
+ file.read(contents, size);
271
+ contents[size] = '\0';
272
+ file.close();
273
+ }
274
+ #endif
275
+ string extension;
276
+ if (path.length() > 5) {
277
+ extension = path.substr(path.length() - 5, 5);
278
+ }
279
+ for(size_t i=0; i<extension.size();++i)
280
+ extension[i] = tolower(extension[i]);
281
+ if (extension == ".sass" && contents != 0) {
282
+ char * converted = sass2scss(contents, SASS2SCSS_PRETTIFY_1);
283
+ delete[] contents; // free the indented contents
284
+ return converted; // should be freed by caller
285
+ } else {
286
+ return contents;
287
+ }
288
+ }
289
+
290
+ }
291
+ }
@@ -0,0 +1,18 @@
1
+ #include <string>
2
+
3
+ namespace Sass {
4
+ using namespace std;
5
+ struct Context;
6
+ namespace File {
7
+ string get_cwd();
8
+ string base_name(string);
9
+ string dir_name(string);
10
+ string join_paths(string, string);
11
+ bool is_absolute_path(const string& path);
12
+ string make_canonical_path (string path);
13
+ string make_absolute_path(const string& path, const string& cwd);
14
+ string resolve_relative_path(const string& uri, const string& base, const string& cwd);
15
+ char* resolve_and_load(string path, string& real_path);
16
+ char* read_file(string path);
17
+ }
18
+ }
@@ -0,0 +1,1565 @@
1
+ #include "functions.hpp"
2
+ #include "ast.hpp"
3
+ #include "context.hpp"
4
+ #include "backtrace.hpp"
5
+ #include "parser.hpp"
6
+ #include "constants.hpp"
7
+ #include "to_string.hpp"
8
+ #include "inspect.hpp"
9
+ #include "eval.hpp"
10
+ #include "util.hpp"
11
+ #include "utf8_string.hpp"
12
+ #include "utf8.h"
13
+
14
+ #include <atomic>
15
+ #include <cstdlib>
16
+ #include <cmath>
17
+ #include <cctype>
18
+ #include <sstream>
19
+ #include <string>
20
+ #include <iomanip>
21
+ #include <iostream>
22
+ #include <random>
23
+ #include <set>
24
+
25
+ #ifdef __MINGW32__
26
+ #include "windows.h"
27
+ #include "wincrypt.h"
28
+ #endif
29
+
30
+ #define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, path, position, backtrace)
31
+ #define ARGR(argname, argtype, lo, hi) get_arg_r(argname, env, sig, path, position, lo, hi, backtrace)
32
+ #define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, path, position, backtrace, ctx)
33
+
34
+ namespace Sass {
35
+ using std::stringstream;
36
+ using std::endl;
37
+
38
+ Definition* make_native_function(Signature sig, Native_Function f, Context& ctx)
39
+ {
40
+ Parser sig_parser = Parser::from_c_str(sig, ctx, "[built-in function]");
41
+ sig_parser.lex<Prelexer::identifier>();
42
+ string name(Util::normalize_underscores(sig_parser.lexed));
43
+ Parameters* params = sig_parser.parse_parameters();
44
+ return new (ctx.mem) Definition("[built-in function]",
45
+ Position(),
46
+ sig,
47
+ name,
48
+ params,
49
+ f,
50
+ false);
51
+ }
52
+
53
+ Definition* make_c_function(Signature sig, Sass_C_Function f, void* cookie, Context& ctx)
54
+ {
55
+ Parser sig_parser = Parser::from_c_str(sig, ctx, "[c function]");
56
+ // allow to overload generic callback plus @warn, @error and @debug with custom functions
57
+ sig_parser.lex < alternatives < identifier, exactly <'*'>,
58
+ exactly < Constants::warn_kwd >,
59
+ exactly < Constants::error_kwd >,
60
+ exactly < Constants::debug_kwd >
61
+ > >();
62
+ string name(Util::normalize_underscores(sig_parser.lexed));
63
+ Parameters* params = sig_parser.parse_parameters();
64
+ return new (ctx.mem) Definition("[c function]",
65
+ Position(),
66
+ sig,
67
+ name,
68
+ params,
69
+ f,
70
+ cookie,
71
+ false, true);
72
+ }
73
+
74
+ namespace Functions {
75
+
76
+ template <typename T>
77
+ T* get_arg(const string& argname, Env& env, Signature sig, const string& path, Position position, Backtrace* backtrace)
78
+ {
79
+ // Minimal error handling -- the expectation is that built-ins will be written correctly!
80
+ T* val = dynamic_cast<T*>(env[argname]);
81
+ if (!val) {
82
+ string msg("argument `");
83
+ msg += argname;
84
+ msg += "` of `";
85
+ msg += sig;
86
+ msg += "` must be a ";
87
+ msg += T::type_name();
88
+ error(msg, path, position, backtrace);
89
+ }
90
+ return val;
91
+ }
92
+
93
+ Map* get_arg_m(const string& argname, Env& env, Signature sig, const string& path, Position position, Backtrace* backtrace, Context& ctx)
94
+ {
95
+ // Minimal error handling -- the expectation is that built-ins will be written correctly!
96
+ Map* val = dynamic_cast<Map*>(env[argname]);
97
+ if (val) return val;
98
+
99
+ List* lval = dynamic_cast<List*>(env[argname]);
100
+ if (lval && lval->length() == 0) return new (ctx.mem) Map(path, position, 0);
101
+
102
+ // fallback on get_arg for error handling
103
+ val = get_arg<Map>(argname, env, sig, path, position, backtrace);
104
+ return val;
105
+ }
106
+
107
+ Number* get_arg_r(const string& argname, Env& env, Signature sig, const string& path, Position position, double lo, double hi, Backtrace* backtrace)
108
+ {
109
+ // Minimal error handling -- the expectation is that built-ins will be written correctly!
110
+ Number* val = get_arg<Number>(argname, env, sig, path, position, backtrace);
111
+ double v = val->value();
112
+ if (!(lo <= v && v <= hi)) {
113
+ stringstream msg;
114
+ msg << "argument `" << argname << "` of `" << sig << "` must be between ";
115
+ msg << lo << " and " << hi;
116
+ error(msg.str(), path, position, backtrace);
117
+ }
118
+ return val;
119
+ }
120
+
121
+ #ifdef __MINGW32__
122
+ uint64_t GetSeed()
123
+ {
124
+ HCRYPTPROV hp = 0;
125
+ BYTE rb[8];
126
+ CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
127
+ CryptGenRandom(hp, sizeof(rb), rb);
128
+ CryptReleaseContext(hp, 0);
129
+
130
+ uint64_t seed;
131
+ memcpy(&seed, &rb[0], sizeof(seed));
132
+
133
+ return seed;
134
+ }
135
+ #else
136
+ static random_device rd;
137
+ uint64_t GetSeed()
138
+ {
139
+ return rd();
140
+ }
141
+ #endif
142
+
143
+ // note: the performance of many implementations of
144
+ // random_device degrades sharply once the entropy pool
145
+ // is exhausted. For practical use, random_device is
146
+ // generally only used to seed a PRNG such as mt19937.
147
+ static mt19937 rand(GetSeed());
148
+
149
+ // features
150
+ static set<string> features;
151
+
152
+ ////////////////
153
+ // RGB FUNCTIONS
154
+ ////////////////
155
+
156
+ Signature rgb_sig = "rgb($red, $green, $blue)";
157
+ BUILT_IN(rgb)
158
+ {
159
+ return new (ctx.mem) Color(path,
160
+ position,
161
+ ARGR("$red", Number, 0, 255)->value(),
162
+ ARGR("$green", Number, 0, 255)->value(),
163
+ ARGR("$blue", Number, 0, 255)->value());
164
+ }
165
+
166
+ Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";
167
+ BUILT_IN(rgba_4)
168
+ {
169
+ return new (ctx.mem) Color(path,
170
+ position,
171
+ ARGR("$red", Number, 0, 255)->value(),
172
+ ARGR("$green", Number, 0, 255)->value(),
173
+ ARGR("$blue", Number, 0, 255)->value(),
174
+ ARGR("$alpha", Number, 0, 1)->value());
175
+ }
176
+
177
+ Signature rgba_2_sig = "rgba($color, $alpha)";
178
+ BUILT_IN(rgba_2)
179
+ {
180
+ Color* c_arg = ARG("$color", Color);
181
+ Color* new_c = new (ctx.mem) Color(*c_arg);
182
+ new_c->a(ARGR("$alpha", Number, 0, 1)->value());
183
+ new_c->disp("");
184
+ return new_c;
185
+ }
186
+
187
+ Signature red_sig = "red($color)";
188
+ BUILT_IN(red)
189
+ { return new (ctx.mem) Number(path, position, ARG("$color", Color)->r()); }
190
+
191
+ Signature green_sig = "green($color)";
192
+ BUILT_IN(green)
193
+ { return new (ctx.mem) Number(path, position, ARG("$color", Color)->g()); }
194
+
195
+ Signature blue_sig = "blue($color)";
196
+ BUILT_IN(blue)
197
+ { return new (ctx.mem) Number(path, position, ARG("$color", Color)->b()); }
198
+
199
+ Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)";
200
+ BUILT_IN(mix)
201
+ {
202
+ Color* color1 = ARG("$color-1", Color);
203
+ Color* color2 = ARG("$color-2", Color);
204
+ Number* weight = ARGR("$weight", Number, 0, 100);
205
+
206
+ double p = weight->value()/100;
207
+ double w = 2*p - 1;
208
+ double a = color1->a() - color2->a();
209
+
210
+ double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0;
211
+ double w2 = 1 - w1;
212
+
213
+ return new (ctx.mem) Color(path,
214
+ position,
215
+ std::floor(w1*color1->r() + w2*color2->r()),
216
+ std::floor(w1*color1->g() + w2*color2->g()),
217
+ std::floor(w1*color1->b() + w2*color2->b()),
218
+ color1->a()*p + color2->a()*(1-p));
219
+ }
220
+
221
+ ////////////////
222
+ // HSL FUNCTIONS
223
+ ////////////////
224
+
225
+ // RGB to HSL helper function
226
+ struct HSL { double h; double s; double l; };
227
+ HSL rgb_to_hsl(double r, double g, double b)
228
+ {
229
+
230
+ // Algorithm from http://en.wikipedia.org/wiki/wHSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
231
+ r /= 255.0; g /= 255.0; b /= 255.0;
232
+
233
+ double max = std::max(r, std::max(g, b));
234
+ double min = std::min(r, std::min(g, b));
235
+ double del = max - min;
236
+
237
+ double h = 0, s = 0, l = (max + min) / 2.0;
238
+
239
+ if (max == min) {
240
+ h = s = 0; // achromatic
241
+ }
242
+ else {
243
+ if (l < 0.5) s = del / (2.0 * l);
244
+ else s = del / (2.0 - 2.0 * l);
245
+
246
+ if (r == max) h = 60 * (g - b) / del;
247
+ else if (g == max) h = 60 * (b - r) / del + 120;
248
+ else if (b == max) h = 60 * (r - g) / del + 240;
249
+ }
250
+
251
+ HSL hsl_struct;
252
+ hsl_struct.h = h;
253
+ hsl_struct.s = s * 100;
254
+ hsl_struct.l = l * 100;
255
+
256
+ return hsl_struct;
257
+ }
258
+
259
+ // hue to RGB helper function
260
+ double h_to_rgb(double m1, double m2, double h) {
261
+ if (h < 0) h += 1;
262
+ if (h > 1) h -= 1;
263
+ if (h*6.0 < 1) return m1 + (m2 - m1)*h*6;
264
+ if (h*2.0 < 1) return m2;
265
+ if (h*3.0 < 2) return m1 + (m2 - m1) * (2.0/3.0 - h)*6;
266
+ return m1;
267
+ }
268
+
269
+ Color* hsla_impl(double h, double s, double l, double a, Context& ctx, const string& path, Position position)
270
+ {
271
+ h /= 360.0;
272
+ s /= 100.0;
273
+ l /= 100.0;
274
+
275
+ // Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color.
276
+ double m2;
277
+ if (l <= 0.5) m2 = l*(s+1.0);
278
+ else m2 = (l+s)-(l*s);
279
+ double m1 = (l*2)-m2;
280
+ // round the results -- consider moving this into the Color constructor
281
+ double r = (h_to_rgb(m1, m2, h+1.0/3.0) * 255.0);
282
+ double g = (h_to_rgb(m1, m2, h) * 255.0);
283
+ double b = (h_to_rgb(m1, m2, h-1.0/3.0) * 255.0);
284
+
285
+ return new (ctx.mem) Color(path, position, r, g, b, a);
286
+ }
287
+
288
+ Signature hsl_sig = "hsl($hue, $saturation, $lightness)";
289
+ BUILT_IN(hsl)
290
+ {
291
+ return hsla_impl(ARG("$hue", Number)->value(),
292
+ ARGR("$saturation", Number, 0, 100)->value(),
293
+ ARGR("$lightness", Number, 0, 100)->value(),
294
+ 1.0,
295
+ ctx,
296
+ path,
297
+ position);
298
+ }
299
+
300
+ Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)";
301
+ BUILT_IN(hsla)
302
+ {
303
+ return hsla_impl(ARG("$hue", Number)->value(),
304
+ ARGR("$saturation", Number, 0, 100)->value(),
305
+ ARGR("$lightness", Number, 0, 100)->value(),
306
+ ARGR("$alpha", Number, 0, 1)->value(),
307
+ ctx,
308
+ path,
309
+ position);
310
+ }
311
+
312
+ Signature hue_sig = "hue($color)";
313
+ BUILT_IN(hue)
314
+ {
315
+ Color* rgb_color = ARG("$color", Color);
316
+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),
317
+ rgb_color->g(),
318
+ rgb_color->b());
319
+ return new (ctx.mem) Number(path, position, hsl_color.h, "deg");
320
+ }
321
+
322
+ Signature saturation_sig = "saturation($color)";
323
+ BUILT_IN(saturation)
324
+ {
325
+ Color* rgb_color = ARG("$color", Color);
326
+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),
327
+ rgb_color->g(),
328
+ rgb_color->b());
329
+ return new (ctx.mem) Number(path, position, hsl_color.s, "%");
330
+ }
331
+
332
+ Signature lightness_sig = "lightness($color)";
333
+ BUILT_IN(lightness)
334
+ {
335
+ Color* rgb_color = ARG("$color", Color);
336
+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),
337
+ rgb_color->g(),
338
+ rgb_color->b());
339
+ return new (ctx.mem) Number(path, position, hsl_color.l, "%");
340
+ }
341
+
342
+ Signature adjust_hue_sig = "adjust-hue($color, $degrees)";
343
+ BUILT_IN(adjust_hue)
344
+ {
345
+ Color* rgb_color = ARG("$color", Color);
346
+ Number* degrees = ARG("$degrees", Number);
347
+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),
348
+ rgb_color->g(),
349
+ rgb_color->b());
350
+ return hsla_impl(hsl_color.h + degrees->value(),
351
+ hsl_color.s,
352
+ hsl_color.l,
353
+ rgb_color->a(),
354
+ ctx,
355
+ path,
356
+ position);
357
+ }
358
+
359
+ Signature lighten_sig = "lighten($color, $amount)";
360
+ BUILT_IN(lighten)
361
+ {
362
+ Color* rgb_color = ARG("$color", Color);
363
+ Number* amount = ARGR("$amount", Number, 0, 100);
364
+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),
365
+ rgb_color->g(),
366
+ rgb_color->b());
367
+ //Check lightness is not negative before lighten it
368
+ double hslcolorL = hsl_color.l;
369
+ if (hslcolorL < 0) {
370
+ hslcolorL = 0;
371
+ }
372
+
373
+ return hsla_impl(hsl_color.h,
374
+ hsl_color.s,
375
+ hslcolorL + amount->value(),
376
+ rgb_color->a(),
377
+ ctx,
378
+ path,
379
+ position);
380
+ }
381
+
382
+ Signature darken_sig = "darken($color, $amount)";
383
+ BUILT_IN(darken)
384
+ {
385
+ Color* rgb_color = ARG("$color", Color);
386
+ Number* amount = ARGR("$amount", Number, 0, 100);
387
+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),
388
+ rgb_color->g(),
389
+ rgb_color->b());
390
+
391
+ //Check lightness if not over 100, before darken it
392
+ double hslcolorL = hsl_color.l;
393
+ if (hslcolorL > 100) {
394
+ hslcolorL = 100;
395
+ }
396
+
397
+ return hsla_impl(hsl_color.h,
398
+ hsl_color.s,
399
+ hslcolorL - amount->value(),
400
+ rgb_color->a(),
401
+ ctx,
402
+ path,
403
+ position);
404
+ }
405
+
406
+ Signature saturate_sig = "saturate($color, $amount: false)";
407
+ BUILT_IN(saturate)
408
+ {
409
+ // CSS3 filter function overload: pass literal through directly
410
+ Number* amount = dynamic_cast<Number*>(env["$amount"]);
411
+ if (!amount) {
412
+ To_String to_string(&ctx);
413
+ return new (ctx.mem) String_Constant(path, position, "saturate(" + env["$color"]->perform(&to_string) + ")");
414
+ }
415
+
416
+ ARGR("$amount", Number, 0, 100);
417
+ Color* rgb_color = ARG("$color", Color);
418
+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),
419
+ rgb_color->g(),
420
+ rgb_color->b());
421
+ //Check saturation is not negative before saturate it
422
+ double hslcolorS = hsl_color.s;
423
+ if (hslcolorS < 0) {
424
+ hslcolorS = 0;
425
+ }
426
+
427
+
428
+ return hsla_impl(hsl_color.h,
429
+ hslcolorS + amount->value(),
430
+ hsl_color.l,
431
+ rgb_color->a(),
432
+ ctx,
433
+ path,
434
+ position);
435
+ }
436
+
437
+ Signature desaturate_sig = "desaturate($color, $amount)";
438
+ BUILT_IN(desaturate)
439
+ {
440
+ Color* rgb_color = ARG("$color", Color);
441
+ Number* amount = ARGR("$amount", Number, 0, 100);
442
+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),
443
+ rgb_color->g(),
444
+ rgb_color->b());
445
+ //Check saturation is not over 100 before desaturate it
446
+ double hslcolorS = hsl_color.s;
447
+ if (hslcolorS > 100) {
448
+ hslcolorS = 100;
449
+ }
450
+
451
+ return hsla_impl(hsl_color.h,
452
+ hslcolorS - amount->value(),
453
+ hsl_color.l,
454
+ rgb_color->a(),
455
+ ctx,
456
+ path,
457
+ position);
458
+ }
459
+
460
+ Signature grayscale_sig = "grayscale($color)";
461
+ BUILT_IN(grayscale)
462
+ {
463
+ // CSS3 filter function overload: pass literal through directly
464
+ Number* amount = dynamic_cast<Number*>(env["$color"]);
465
+ if (amount) {
466
+ To_String to_string(&ctx);
467
+ return new (ctx.mem) String_Constant(path, position, "grayscale(" + amount->perform(&to_string) + ")");
468
+ }
469
+
470
+ Color* rgb_color = ARG("$color", Color);
471
+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),
472
+ rgb_color->g(),
473
+ rgb_color->b());
474
+ return hsla_impl(hsl_color.h,
475
+ 0.0,
476
+ hsl_color.l,
477
+ rgb_color->a(),
478
+ ctx,
479
+ path,
480
+ position);
481
+ }
482
+
483
+ Signature complement_sig = "complement($color)";
484
+ BUILT_IN(complement)
485
+ {
486
+ Color* rgb_color = ARG("$color", Color);
487
+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),
488
+ rgb_color->g(),
489
+ rgb_color->b());
490
+ return hsla_impl(hsl_color.h - 180.0,
491
+ hsl_color.s,
492
+ hsl_color.l,
493
+ rgb_color->a(),
494
+ ctx,
495
+ path,
496
+ position);
497
+ }
498
+
499
+ Signature invert_sig = "invert($color)";
500
+ BUILT_IN(invert)
501
+ {
502
+ // CSS3 filter function overload: pass literal through directly
503
+ Number* amount = dynamic_cast<Number*>(env["$color"]);
504
+ if (amount) {
505
+ To_String to_string(&ctx);
506
+ return new (ctx.mem) String_Constant(path, position, "invert(" + amount->perform(&to_string) + ")");
507
+ }
508
+
509
+ Color* rgb_color = ARG("$color", Color);
510
+ return new (ctx.mem) Color(path,
511
+ position,
512
+ 255 - rgb_color->r(),
513
+ 255 - rgb_color->g(),
514
+ 255 - rgb_color->b(),
515
+ rgb_color->a());
516
+ }
517
+
518
+ ////////////////////
519
+ // OPACITY FUNCTIONS
520
+ ////////////////////
521
+ Signature alpha_sig = "alpha($color)";
522
+ Signature opacity_sig = "opacity($color)";
523
+ BUILT_IN(alpha)
524
+ {
525
+ String_Constant* ie_kwd = dynamic_cast<String_Constant*>(env["$color"]);
526
+ if (ie_kwd) {
527
+ return new (ctx.mem) String_Constant(path, position, "alpha(" + ie_kwd->value() + ")");
528
+ }
529
+
530
+ // CSS3 filter function overload: pass literal through directly
531
+ Number* amount = dynamic_cast<Number*>(env["$color"]);
532
+ if (amount) {
533
+ To_String to_string(&ctx);
534
+ return new (ctx.mem) String_Constant(path, position, "opacity(" + amount->perform(&to_string) + ")");
535
+ }
536
+
537
+ return new (ctx.mem) Number(path, position, ARG("$color", Color)->a());
538
+ }
539
+
540
+ Signature opacify_sig = "opacify($color, $amount)";
541
+ Signature fade_in_sig = "fade-in($color, $amount)";
542
+ BUILT_IN(opacify)
543
+ {
544
+ Color* color = ARG("$color", Color);
545
+ double alpha = color->a() + ARGR("$amount", Number, 0, 1)->value();
546
+ return new (ctx.mem) Color(path,
547
+ position,
548
+ color->r(),
549
+ color->g(),
550
+ color->b(),
551
+ alpha > 1.0 ? 1.0 : alpha);
552
+ }
553
+
554
+ Signature transparentize_sig = "transparentize($color, $amount)";
555
+ Signature fade_out_sig = "fade-out($color, $amount)";
556
+ BUILT_IN(transparentize)
557
+ {
558
+ Color* color = ARG("$color", Color);
559
+ double alpha = color->a() - ARGR("$amount", Number, 0, 1)->value();
560
+ return new (ctx.mem) Color(path,
561
+ position,
562
+ color->r(),
563
+ color->g(),
564
+ color->b(),
565
+ alpha < 0.0 ? 0.0 : alpha);
566
+ }
567
+
568
+ ////////////////////////
569
+ // OTHER COLOR FUNCTIONS
570
+ ////////////////////////
571
+
572
+ Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";
573
+ BUILT_IN(adjust_color)
574
+ {
575
+ Color* color = ARG("$color", Color);
576
+ Number* r = dynamic_cast<Number*>(env["$red"]);
577
+ Number* g = dynamic_cast<Number*>(env["$green"]);
578
+ Number* b = dynamic_cast<Number*>(env["$blue"]);
579
+ Number* h = dynamic_cast<Number*>(env["$hue"]);
580
+ Number* s = dynamic_cast<Number*>(env["$saturation"]);
581
+ Number* l = dynamic_cast<Number*>(env["$lightness"]);
582
+ Number* a = dynamic_cast<Number*>(env["$alpha"]);
583
+
584
+ bool rgb = r || g || b;
585
+ bool hsl = h || s || l;
586
+
587
+ if (rgb && hsl) {
588
+ error("cannot specify both RGB and HSL values for `adjust-color`", path, position);
589
+ }
590
+ if (rgb) {
591
+ return new (ctx.mem) Color(path,
592
+ position,
593
+ color->r() + (r ? r->value() : 0),
594
+ color->g() + (g ? g->value() : 0),
595
+ color->b() + (b ? b->value() : 0),
596
+ color->a() + (a ? a->value() : 0));
597
+ }
598
+ if (hsl) {
599
+ HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());
600
+ return hsla_impl(hsl_struct.h + (h ? h->value() : 0),
601
+ hsl_struct.s + (s ? s->value() : 0),
602
+ hsl_struct.l + (l ? l->value() : 0),
603
+ color->a() + (a ? a->value() : 0),
604
+ ctx,
605
+ path,
606
+ position);
607
+ }
608
+ if (a) {
609
+ return new (ctx.mem) Color(path,
610
+ position,
611
+ color->r(),
612
+ color->g(),
613
+ color->b(),
614
+ color->a() + (a ? a->value() : 0));
615
+ }
616
+ error("not enough arguments for `adjust-color`", path, position);
617
+ // unreachable
618
+ return color;
619
+ }
620
+
621
+ Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";
622
+ BUILT_IN(scale_color)
623
+ {
624
+ Color* color = ARG("$color", Color);
625
+ Number* r = dynamic_cast<Number*>(env["$red"]);
626
+ Number* g = dynamic_cast<Number*>(env["$green"]);
627
+ Number* b = dynamic_cast<Number*>(env["$blue"]);
628
+ Number* h = dynamic_cast<Number*>(env["$hue"]);
629
+ Number* s = dynamic_cast<Number*>(env["$saturation"]);
630
+ Number* l = dynamic_cast<Number*>(env["$lightness"]);
631
+ Number* a = dynamic_cast<Number*>(env["$alpha"]);
632
+
633
+ bool rgb = r || g || b;
634
+ bool hsl = h || s || l;
635
+
636
+ if (rgb && hsl) {
637
+ error("cannot specify both RGB and HSL values for `scale-color`", path, position);
638
+ }
639
+ if (rgb) {
640
+ double rscale = (r ? ARGR("$red", Number, -100.0, 100.0)->value() : 0.0) / 100.0;
641
+ double gscale = (g ? ARGR("$green", Number, -100.0, 100.0)->value() : 0.0) / 100.0;
642
+ double bscale = (b ? ARGR("$blue", Number, -100.0, 100.0)->value() : 0.0) / 100.0;
643
+ double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0;
644
+ return new (ctx.mem) Color(path,
645
+ position,
646
+ color->r() + rscale * (rscale > 0.0 ? 255 - color->r() : color->r()),
647
+ color->g() + gscale * (gscale > 0.0 ? 255 - color->g() : color->g()),
648
+ color->b() + bscale * (bscale > 0.0 ? 255 - color->b() : color->b()),
649
+ color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a()));
650
+ }
651
+ if (hsl) {
652
+ double hscale = (h ? ARGR("$hue", Number, -100.0, 100.0)->value() : 0.0) / 100.0;
653
+ double sscale = (s ? ARGR("$saturation", Number, -100.0, 100.0)->value() : 0.0) / 100.0;
654
+ double lscale = (l ? ARGR("$lightness", Number, -100.0, 100.0)->value() : 0.0) / 100.0;
655
+ double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0;
656
+ HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());
657
+ hsl_struct.h += hscale * (hscale > 0.0 ? 360.0 - hsl_struct.h : hsl_struct.h);
658
+ hsl_struct.s += sscale * (sscale > 0.0 ? 100.0 - hsl_struct.s : hsl_struct.s);
659
+ hsl_struct.l += lscale * (lscale > 0.0 ? 100.0 - hsl_struct.l : hsl_struct.l);
660
+ double alpha = color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->r());
661
+ return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, path, position);
662
+ }
663
+ if (a) {
664
+ double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0;
665
+ return new (ctx.mem) Color(path,
666
+ position,
667
+ color->r(),
668
+ color->g(),
669
+ color->b(),
670
+ color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a()));
671
+ }
672
+ error("not enough arguments for `scale-color`", path, position);
673
+ // unreachable
674
+ return color;
675
+ }
676
+
677
+ Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";
678
+ BUILT_IN(change_color)
679
+ {
680
+ Color* color = ARG("$color", Color);
681
+ Number* r = dynamic_cast<Number*>(env["$red"]);
682
+ Number* g = dynamic_cast<Number*>(env["$green"]);
683
+ Number* b = dynamic_cast<Number*>(env["$blue"]);
684
+ Number* h = dynamic_cast<Number*>(env["$hue"]);
685
+ Number* s = dynamic_cast<Number*>(env["$saturation"]);
686
+ Number* l = dynamic_cast<Number*>(env["$lightness"]);
687
+ Number* a = dynamic_cast<Number*>(env["$alpha"]);
688
+
689
+ bool rgb = r || g || b;
690
+ bool hsl = h || s || l;
691
+
692
+ if (rgb && hsl) {
693
+ error("cannot specify both RGB and HSL values for `change-color`", path, position);
694
+ }
695
+ if (rgb) {
696
+ return new (ctx.mem) Color(path,
697
+ position,
698
+ r ? ARGR("$red", Number, 0, 255)->value() : color->r(),
699
+ g ? ARGR("$green", Number, 0, 255)->value() : color->g(),
700
+ b ? ARGR("$blue", Number, 0, 255)->value() : color->b(),
701
+ a ? ARGR("$alpha", Number, 0, 255)->value() : color->a());
702
+ }
703
+ if (hsl) {
704
+ HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());
705
+ if (h) hsl_struct.h = static_cast<double>(((static_cast<int>(h->value()) % 360) + 360) % 360) / 360.0;
706
+ if (s) hsl_struct.s = ARGR("$saturation", Number, 0, 100)->value();
707
+ if (l) hsl_struct.l = ARGR("$lightness", Number, 0, 100)->value();
708
+ double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a();
709
+ return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, path, position);
710
+ }
711
+ if (a) {
712
+ double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a();
713
+ return new (ctx.mem) Color(path,
714
+ position,
715
+ color->r(),
716
+ color->g(),
717
+ color->b(),
718
+ alpha);
719
+ }
720
+ error("not enough arguments for `change-color`", path, position);
721
+ // unreachable
722
+ return color;
723
+ }
724
+
725
+ template <size_t range>
726
+ static double cap_channel(double c) {
727
+ if (c > range) return range;
728
+ else if (c < 0) return 0;
729
+ else return c;
730
+ }
731
+
732
+ Signature ie_hex_str_sig = "ie-hex-str($color)";
733
+ BUILT_IN(ie_hex_str)
734
+ {
735
+ Color* c = ARG("$color", Color);
736
+ double r = cap_channel<0xff>(c->r());
737
+ double g = cap_channel<0xff>(c->g());
738
+ double b = cap_channel<0xff>(c->b());
739
+ double a = cap_channel<1> (c->a()) * 255;
740
+
741
+ stringstream ss;
742
+ ss << '#' << std::setw(2) << std::setfill('0');
743
+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(std::floor(a+0.5));
744
+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(std::floor(r+0.5));
745
+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(std::floor(g+0.5));
746
+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(std::floor(b+0.5));
747
+
748
+ string result(ss.str());
749
+ for (size_t i = 0, L = result.length(); i < L; ++i) {
750
+ result[i] = std::toupper(result[i]);
751
+ }
752
+ return new (ctx.mem) String_Constant(path, position, result);
753
+ }
754
+
755
+ ///////////////////
756
+ // STRING FUNCTIONS
757
+ ///////////////////
758
+
759
+ Signature unquote_sig = "unquote($string)";
760
+ BUILT_IN(sass_unquote)
761
+ {
762
+ To_String to_string;
763
+ AST_Node* arg = env["$string"];
764
+ string org(arg->perform(&to_string));
765
+ string str(unquote(org));
766
+ String_Constant* result = new (ctx.mem) String_Constant(path, position, str);
767
+ // remember if the string was quoted (color tokens)
768
+ if (org[0] != str[0]) result->needs_unquoting(true);
769
+ result->is_delayed(true);
770
+ return result;
771
+ }
772
+
773
+ Signature quote_sig = "quote($string)";
774
+ BUILT_IN(sass_quote)
775
+ {
776
+ To_String to_string;
777
+ AST_Node* arg = env["$string"];
778
+ string str(quote(arg->perform(&to_string), String_Constant::double_quote()));
779
+ String_Constant* result = new (ctx.mem) String_Constant(path, position, str);
780
+ result->is_delayed(true);
781
+ return result;
782
+ }
783
+
784
+
785
+ Signature str_length_sig = "str-length($string)";
786
+ BUILT_IN(str_length)
787
+ {
788
+ size_t len = string::npos;
789
+ try {
790
+ String_Constant* s = ARG("$string", String_Constant);
791
+ string str = s->value();
792
+ size_t length_of_s = str.size();
793
+ size_t i = 0;
794
+
795
+ if (s->is_quoted()) {
796
+ ++i;
797
+ --length_of_s;
798
+ }
799
+
800
+ len = UTF_8::code_point_count(str, i, length_of_s);
801
+
802
+ }
803
+ catch (utf8::invalid_code_point) {
804
+ string msg("utf8::invalid_code_point");
805
+ error(msg, path, position, backtrace);
806
+ }
807
+ catch (utf8::not_enough_room) {
808
+ string msg("utf8::not_enough_room");
809
+ error(msg, path, position, backtrace);
810
+ }
811
+ catch (utf8::invalid_utf8) {
812
+ string msg("utf8::invalid_utf8");
813
+ error(msg, path, position, backtrace);
814
+ }
815
+ catch (...) { throw; }
816
+ // return something even if we had an error (-1)
817
+ return new (ctx.mem) Number(path, position, len);
818
+ }
819
+
820
+ Signature str_insert_sig = "str-insert($string, $insert, $index)";
821
+ BUILT_IN(str_insert)
822
+ {
823
+ string str;
824
+ try {
825
+ String_Constant* s = ARG("$string", String_Constant);
826
+ str = s->value();
827
+ char quotemark = s->quote_mark();
828
+ str = unquote(str);
829
+ String_Constant* i = ARG("$insert", String_Constant);
830
+ string ins = i->value();
831
+ ins = unquote(ins);
832
+ Number* ind = ARG("$index", Number);
833
+ double index = ind->value();
834
+ size_t len = UTF_8::code_point_count(str, 0, str.size());
835
+
836
+ if (index > 0 && index <= len) {
837
+ // positive and within string length
838
+ str.insert(UTF_8::offset_at_position(str, index - 1), ins);
839
+ }
840
+ else if (index > len) {
841
+ // positive and past string length
842
+ str += ins;
843
+ }
844
+ else if (index == 0) {
845
+ str = ins + str;
846
+ }
847
+ else if (std::abs(index) <= len) {
848
+ // negative and within string length
849
+ index += len + 1;
850
+ str.insert(UTF_8::offset_at_position(str, index), ins);
851
+ }
852
+ else {
853
+ // negative and past string length
854
+ str = ins + str;
855
+ }
856
+
857
+ if (quotemark) {
858
+ str = quote(str, String_Constant::double_quote());
859
+ }
860
+ }
861
+ catch (utf8::invalid_code_point) {
862
+ string msg("utf8::invalid_code_point");
863
+ error(msg, path, position, backtrace);
864
+ }
865
+ catch (utf8::not_enough_room) {
866
+ string msg("utf8::not_enough_room");
867
+ error(msg, path, position, backtrace);
868
+ }
869
+ catch (utf8::invalid_utf8) {
870
+ string msg("utf8::invalid_utf8");
871
+ error(msg, path, position, backtrace);
872
+ }
873
+ catch (...) { throw; }
874
+ return new (ctx.mem) String_Constant(path, position, str);
875
+ }
876
+
877
+ Signature str_index_sig = "str-index($string, $substring)";
878
+ BUILT_IN(str_index)
879
+ {
880
+ size_t index = string::npos;
881
+ try {
882
+ String_Constant* s = ARG("$string", String_Constant);
883
+ String_Constant* t = ARG("$substring", String_Constant);
884
+ string str = s->value();
885
+ str = unquote(str);
886
+ string substr = t->value();
887
+ substr = unquote(substr);
888
+
889
+ size_t c_index = str.find(substr);
890
+ if(c_index == string::npos) {
891
+ return new (ctx.mem) Null(path, position);
892
+ }
893
+ index = UTF_8::code_point_count(str, 0, c_index) + 1;
894
+ }
895
+ catch (utf8::invalid_code_point) {
896
+ string msg("utf8::invalid_code_point");
897
+ error(msg, path, position, backtrace);
898
+ }
899
+ catch (utf8::not_enough_room) {
900
+ string msg("utf8::not_enough_room");
901
+ error(msg, path, position, backtrace);
902
+ }
903
+ catch (utf8::invalid_utf8) {
904
+ string msg("utf8::invalid_utf8");
905
+ error(msg, path, position, backtrace);
906
+ }
907
+ catch (...) { throw; }
908
+ // return something even if we had an error (-1)
909
+ return new (ctx.mem) Number(path, position, index);
910
+ }
911
+
912
+ Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)";
913
+ BUILT_IN(str_slice)
914
+ {
915
+ string newstr;
916
+ try {
917
+ String_Constant* s = ARG("$string", String_Constant);
918
+ Number* n = ARG("$start-at", Number);
919
+ Number* m = ARG("$end-at", Number);
920
+
921
+ string str = s->value();
922
+ char quotemark = s->quote_mark();
923
+ str = unquote(str);
924
+
925
+ // normalize into 0-based indices
926
+ size_t start = UTF_8::offset_at_position(str, UTF_8::normalize_index(n->value(), UTF_8::code_point_count(str)));
927
+ size_t end = UTF_8::offset_at_position(str, UTF_8::normalize_index(m->value(), UTF_8::code_point_count(str)));
928
+
929
+ // `str-slice` should always return an empty string when $end-at == 0
930
+ // `normalize_index` normalizes 1 -> 0 so we need to check the original value
931
+ if(start == end && m->value() > 0) {
932
+ newstr = str.substr(start, 1);
933
+ } else if(end > start) {
934
+ newstr = str.substr(start, end - start + UTF_8::code_point_size_at_offset(str, end));
935
+ }
936
+ if(quotemark) {
937
+ newstr = quote(newstr, String_Constant::double_quote());
938
+ }
939
+ }
940
+ catch (utf8::invalid_code_point) {
941
+ string msg("utf8::invalid_code_point");
942
+ error(msg, path, position, backtrace);
943
+ }
944
+ catch (utf8::not_enough_room) {
945
+ string msg("utf8::not_enough_room");
946
+ error(msg, path, position, backtrace);
947
+ }
948
+ catch (utf8::invalid_utf8) {
949
+ string msg("utf8::invalid_utf8");
950
+ error(msg, path, position, backtrace);
951
+ }
952
+ catch (...) { throw; }
953
+ return new (ctx.mem) String_Constant(path, position, newstr);
954
+ }
955
+
956
+ Signature to_upper_case_sig = "to-upper-case($string)";
957
+ BUILT_IN(to_upper_case)
958
+ {
959
+ String_Constant* s = ARG("$string", String_Constant);
960
+ string str = s->value();
961
+
962
+ for (size_t i = 0, L = str.length(); i < L; ++i) {
963
+ if (Sass::Util::isAscii(str[i])) {
964
+ str[i] = std::toupper(str[i]);
965
+ }
966
+ }
967
+
968
+ return new (ctx.mem) String_Constant(path, position, str);
969
+ }
970
+
971
+ Signature to_lower_case_sig = "to-lower-case($string)";
972
+ BUILT_IN(to_lower_case)
973
+ {
974
+ String_Constant* s = ARG("$string", String_Constant);
975
+ string str = s->value();
976
+
977
+ for (size_t i = 0, L = str.length(); i < L; ++i) {
978
+ if (Sass::Util::isAscii(str[i])) {
979
+ str[i] = std::tolower(str[i]);
980
+ }
981
+ }
982
+
983
+ return new (ctx.mem) String_Constant(path, position, str);
984
+ }
985
+
986
+ ///////////////////
987
+ // NUMBER FUNCTIONS
988
+ ///////////////////
989
+
990
+ Signature percentage_sig = "percentage($value)";
991
+ BUILT_IN(percentage)
992
+ {
993
+ Number* n = ARG("$value", Number);
994
+ if (!n->is_unitless()) error("argument $value of `" + string(sig) + "` must be unitless", path, position);
995
+ return new (ctx.mem) Number(path, position, n->value() * 100, "%");
996
+ }
997
+
998
+ Signature round_sig = "round($value)";
999
+ BUILT_IN(round)
1000
+ {
1001
+ Number* n = ARG("$value", Number);
1002
+ Number* r = new (ctx.mem) Number(*n);
1003
+ r->path(path);
1004
+ r->position(position);
1005
+ r->value(std::floor(r->value() + 0.5));
1006
+ return r;
1007
+ }
1008
+
1009
+ Signature ceil_sig = "ceil($value)";
1010
+ BUILT_IN(ceil)
1011
+ {
1012
+ Number* n = ARG("$value", Number);
1013
+ Number* r = new (ctx.mem) Number(*n);
1014
+ r->path(path);
1015
+ r->position(position);
1016
+ r->value(std::ceil(r->value()));
1017
+ return r;
1018
+ }
1019
+
1020
+ Signature floor_sig = "floor($value)";
1021
+ BUILT_IN(floor)
1022
+ {
1023
+ Number* n = ARG("$value", Number);
1024
+ Number* r = new (ctx.mem) Number(*n);
1025
+ r->path(path);
1026
+ r->position(position);
1027
+ r->value(std::floor(r->value()));
1028
+ return r;
1029
+ }
1030
+
1031
+ Signature abs_sig = "abs($value)";
1032
+ BUILT_IN(abs)
1033
+ {
1034
+ Number* n = ARG("$value", Number);
1035
+ Number* r = new (ctx.mem) Number(*n);
1036
+ r->path(path);
1037
+ r->position(position);
1038
+ r->value(std::abs(r->value()));
1039
+ return r;
1040
+ }
1041
+
1042
+ Signature min_sig = "min($x1, $x2...)";
1043
+ BUILT_IN(min)
1044
+ {
1045
+ Number* x1 = ARG("$x1", Number);
1046
+ List* arglist = ARG("$x2", List);
1047
+ Number* least = x1;
1048
+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {
1049
+ Number* xi = dynamic_cast<Number*>(arglist->value_at_index(i));
1050
+ if (!xi) error("`" + string(sig) + "` only takes numeric arguments", path, position);
1051
+ if (lt(xi, least, ctx)) least = xi;
1052
+ }
1053
+ return least;
1054
+ }
1055
+
1056
+ Signature max_sig = "max($x1, $x2...)";
1057
+ BUILT_IN(max)
1058
+ {
1059
+ Number* x1 = ARG("$x1", Number);
1060
+ List* arglist = ARG("$x2", List);
1061
+ Number* greatest = x1;
1062
+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {
1063
+ Number* xi = dynamic_cast<Number*>(arglist->value_at_index(i));
1064
+ if (!xi) error("`" + string(sig) + "` only takes numeric arguments", path, position);
1065
+ if (lt(greatest, xi, ctx)) greatest = xi;
1066
+ }
1067
+ return greatest;
1068
+ }
1069
+
1070
+ Signature random_sig = "random($limit:false)";
1071
+ BUILT_IN(random)
1072
+ {
1073
+ Number* l = dynamic_cast<Number*>(env["$limit"]);
1074
+ if (l && trunc(l->value()) != l->value()) error("argument $limit of `" + string(sig) + "` must be an integer", path, position);
1075
+ if (l) {
1076
+ uniform_real_distribution<> distributor(1, l->value() + 1);
1077
+ uint_fast32_t distributed = distributor(rand);
1078
+ return new (ctx.mem) Number(path, position, (double)distributed);
1079
+ }
1080
+ else {
1081
+ uniform_real_distribution<> distributor(0, 1);
1082
+ uint_fast32_t distributed = distributor(rand);
1083
+ return new (ctx.mem) Number(path, position, trunc(distributed));
1084
+ }
1085
+ }
1086
+
1087
+ /////////////////
1088
+ // LIST FUNCTIONS
1089
+ /////////////////
1090
+
1091
+ Signature length_sig = "length($list)";
1092
+ BUILT_IN(length)
1093
+ {
1094
+ Expression* v = ARG("$list", Expression);
1095
+ if (v->concrete_type() == Expression::MAP) {
1096
+ Map* map = dynamic_cast<Map*>(env["$list"]);
1097
+ return new (ctx.mem) Number(path,
1098
+ position,
1099
+ map ? map->length() : 1);
1100
+ }
1101
+
1102
+ List* list = dynamic_cast<List*>(env["$list"]);
1103
+ return new (ctx.mem) Number(path,
1104
+ position,
1105
+ list ? list->length() : 1);
1106
+ }
1107
+
1108
+ Signature nth_sig = "nth($list, $n)";
1109
+ BUILT_IN(nth)
1110
+ {
1111
+ Map* m = dynamic_cast<Map*>(env["$list"]);
1112
+ List* l = dynamic_cast<List*>(env["$list"]);
1113
+ Number* n = ARG("$n", Number);
1114
+ if (n->value() == 0) error("argument `$n` of `" + string(sig) + "` must be non-zero", path, position);
1115
+ // if the argument isn't a list, then wrap it in a singleton list
1116
+ if (!m && !l) {
1117
+ l = new (ctx.mem) List(path, position, 1);
1118
+ *l << ARG("$list", Expression);
1119
+ }
1120
+ size_t len = m ? m->length() : l->length();
1121
+ bool empty = m ? m->empty() : l->empty();
1122
+ if (empty) error("argument `$list` of `" + string(sig) + "` must not be empty", path, position);
1123
+ double index = std::floor(n->value() < 0 ? len + n->value() : n->value() - 1);
1124
+ if (index < 0 || index > len - 1) error("index out of bounds for `" + string(sig) + "`", path, position);
1125
+
1126
+ if (m) {
1127
+ l = new (ctx.mem) List(path, position, 1);
1128
+ *l << m->keys()[index];
1129
+ *l << m->at(m->keys()[index]);
1130
+ return l;
1131
+ }
1132
+ else {
1133
+ return l->value_at_index(index);
1134
+ }
1135
+ }
1136
+
1137
+ Signature set_nth_sig = "set-nth($list, $n, $value)";
1138
+ BUILT_IN(set_nth)
1139
+ {
1140
+ List* l = dynamic_cast<List*>(env["$list"]);
1141
+ Number* n = ARG("$n", Number);
1142
+ Expression* v = ARG("$value", Expression);
1143
+ if (!l) {
1144
+ l = new (ctx.mem) List(path, position, 1);
1145
+ *l << ARG("$list", Expression);
1146
+ }
1147
+ if (l->empty()) error("argument `$list` of `" + string(sig) + "` must not be empty", path, position);
1148
+ double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1);
1149
+ if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + string(sig) + "`", path, position);
1150
+ List* result = new (ctx.mem) List(path, position, l->length(), l->separator());
1151
+ for (size_t i = 0, L = l->length(); i < L; ++i) {
1152
+ *result << ((i == index) ? v : (*l)[i]);
1153
+ }
1154
+ return result;
1155
+ }
1156
+
1157
+ Signature index_sig = "index($list, $value)";
1158
+ BUILT_IN(index)
1159
+ {
1160
+ List* l = dynamic_cast<List*>(env["$list"]);
1161
+ Expression* v = ARG("$value", Expression);
1162
+ if (!l) {
1163
+ l = new (ctx.mem) List(path, position, 1);
1164
+ *l << ARG("$list", Expression);
1165
+ }
1166
+ for (size_t i = 0, L = l->length(); i < L; ++i) {
1167
+ if (eq(l->value_at_index(i), v, ctx)) return new (ctx.mem) Number(path, position, i+1);
1168
+ }
1169
+ return new (ctx.mem) Null(path, position);
1170
+ }
1171
+
1172
+ Signature join_sig = "join($list1, $list2, $separator: auto)";
1173
+ BUILT_IN(join)
1174
+ {
1175
+ List* l1 = dynamic_cast<List*>(env["$list1"]);
1176
+ List* l2 = dynamic_cast<List*>(env["$list2"]);
1177
+ String_Constant* sep = ARG("$separator", String_Constant);
1178
+ List::Separator sep_val = (l1 ? l1->separator() : List::SPACE);
1179
+ if (!l1) {
1180
+ l1 = new (ctx.mem) List(path, position, 1);
1181
+ *l1 << ARG("$list1", Expression);
1182
+ sep_val = (l2 ? l2->separator() : List::SPACE);
1183
+ }
1184
+ if (!l2) {
1185
+ l2 = new (ctx.mem) List(path, position, 1);
1186
+ *l2 << ARG("$list2", Expression);
1187
+ }
1188
+ size_t len = l1->length() + l2->length();
1189
+ string sep_str = unquote(sep->value());
1190
+ if (sep_str == "space") sep_val = List::SPACE;
1191
+ else if (sep_str == "comma") sep_val = List::COMMA;
1192
+ else if (sep_str != "auto") error("argument `$separator` of `" + string(sig) + "` must be `space`, `comma`, or `auto`", path, position);
1193
+ List* result = new (ctx.mem) List(path, position, len, sep_val);
1194
+ *result += l1;
1195
+ *result += l2;
1196
+ return result;
1197
+ }
1198
+
1199
+ Signature append_sig = "append($list, $val, $separator: auto)";
1200
+ BUILT_IN(append)
1201
+ {
1202
+ List* l = dynamic_cast<List*>(env["$list"]);
1203
+ Expression* v = ARG("$val", Expression);
1204
+ String_Constant* sep = ARG("$separator", String_Constant);
1205
+ if (!l) {
1206
+ l = new (ctx.mem) List(path, position, 1);
1207
+ *l << ARG("$list", Expression);
1208
+ }
1209
+ List* result = new (ctx.mem) List(path, position, l->length() + 1, l->separator());
1210
+ string sep_str(unquote(sep->value()));
1211
+ if (sep_str == "space") result->separator(List::SPACE);
1212
+ else if (sep_str == "comma") result->separator(List::COMMA);
1213
+ else if (sep_str != "auto") error("argument `$separator` of `" + string(sig) + "` must be `space`, `comma`, or `auto`", path, position);
1214
+ *result += l;
1215
+ *result << v;
1216
+ return result;
1217
+ }
1218
+
1219
+ Signature zip_sig = "zip($lists...)";
1220
+ BUILT_IN(zip)
1221
+ {
1222
+ List* arglist = new (ctx.mem) List(*ARG("$lists", List));
1223
+ size_t shortest = 0;
1224
+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {
1225
+ List* ith = dynamic_cast<List*>(arglist->value_at_index(i));
1226
+ if (!ith) {
1227
+ ith = new (ctx.mem) List(path, position, 1);
1228
+ *ith << arglist->value_at_index(i);
1229
+ if (arglist->is_arglist()) {
1230
+ ((Argument*)(*arglist)[i])->value(ith);
1231
+ } else {
1232
+ (*arglist)[i] = ith;
1233
+ }
1234
+ }
1235
+ shortest = (i ? std::min(shortest, ith->length()) : ith->length());
1236
+ }
1237
+ List* zippers = new (ctx.mem) List(path, position, shortest, List::COMMA);
1238
+ size_t L = arglist->length();
1239
+ for (size_t i = 0; i < shortest; ++i) {
1240
+ List* zipper = new (ctx.mem) List(path, position, L);
1241
+ for (size_t j = 0; j < L; ++j) {
1242
+ *zipper << (*static_cast<List*>(arglist->value_at_index(j)))[i];
1243
+ }
1244
+ *zippers << zipper;
1245
+ }
1246
+ return zippers;
1247
+ }
1248
+
1249
+ Signature compact_sig = "compact($values...)";
1250
+ BUILT_IN(compact)
1251
+ {
1252
+ List* arglist = ARG("$values", List);
1253
+ List::Separator sep = List::COMMA;
1254
+ if (arglist->length() == 1) {
1255
+ Expression* the_arg = arglist->value_at_index(0);
1256
+ arglist = dynamic_cast<List*>(the_arg);
1257
+ if (!arglist) {
1258
+ List* result = new (ctx.mem) List(path, position, 1, List::COMMA);
1259
+ *result << the_arg;
1260
+ return result;
1261
+ }
1262
+ sep = arglist->separator();
1263
+ }
1264
+ List* result = new (ctx.mem) List(path, position, 0, sep);
1265
+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {
1266
+ Boolean* ith = dynamic_cast<Boolean*>(arglist->value_at_index(i));
1267
+ if (ith && ith->value() == false) continue;
1268
+ *result << arglist->value_at_index(i);
1269
+ }
1270
+ return result;
1271
+ }
1272
+
1273
+ Signature list_separator_sig = "list_separator($list)";
1274
+ BUILT_IN(list_separator)
1275
+ {
1276
+ List* l = dynamic_cast<List*>(env["$list"]);
1277
+ if (!l) {
1278
+ l = new (ctx.mem) List(path, position, 1);
1279
+ *l << ARG("$list", Expression);
1280
+ }
1281
+ return new (ctx.mem) String_Constant(path,
1282
+ position,
1283
+ l->separator() == List::COMMA ? "comma" : "space");
1284
+ }
1285
+
1286
+ /////////////////
1287
+ // MAP FUNCTIONS
1288
+ /////////////////
1289
+
1290
+ Signature map_get_sig = "map-get($map, $key)";
1291
+ BUILT_IN(map_get)
1292
+ {
1293
+ Map* m = ARGM("$map", Map, ctx);
1294
+ Expression* v = ARG("$key", Expression);
1295
+ try {
1296
+ return m->at(v);
1297
+ } catch (const std::out_of_range&) {
1298
+ return new (ctx.mem) Null(path, position);
1299
+ }
1300
+ catch (...) { throw; }
1301
+ }
1302
+
1303
+ Signature map_has_key_sig = "map-has-key($map, $key)";
1304
+ BUILT_IN(map_has_key)
1305
+ {
1306
+ Map* m = ARGM("$map", Map, ctx);
1307
+ Expression* v = ARG("$key", Expression);
1308
+ return new (ctx.mem) Boolean(path, position, m->has(v));
1309
+ }
1310
+
1311
+ Signature map_keys_sig = "map-keys($map)";
1312
+ BUILT_IN(map_keys)
1313
+ {
1314
+ Map* m = ARGM("$map", Map, ctx);
1315
+ List* result = new (ctx.mem) List(path, position, m->length(), List::COMMA);
1316
+ for ( auto key : m->keys()) {
1317
+ *result << key;
1318
+ }
1319
+ return result;
1320
+ }
1321
+
1322
+ Signature map_values_sig = "map-values($map)";
1323
+ BUILT_IN(map_values)
1324
+ {
1325
+ Map* m = ARGM("$map", Map, ctx);
1326
+ List* result = new (ctx.mem) List(path, position, m->length(), List::COMMA);
1327
+ for ( auto key : m->keys()) {
1328
+ *result << m->at(key);
1329
+ }
1330
+ return result;
1331
+ }
1332
+
1333
+ Signature map_merge_sig = "map-merge($map1, $map2)";
1334
+ BUILT_IN(map_merge)
1335
+ {
1336
+ Map* m1 = ARGM("$map1", Map, ctx);
1337
+ Map* m2 = ARGM("$map2", Map, ctx);
1338
+
1339
+ size_t len = m1->length() + m2->length();
1340
+ Map* result = new (ctx.mem) Map(path, position, len);
1341
+ *result += m1;
1342
+ *result += m2;
1343
+ return result;
1344
+ }
1345
+
1346
+ Signature map_remove_sig = "map-remove($map, $keys...)";
1347
+ BUILT_IN(map_remove)
1348
+ {
1349
+ bool remove;
1350
+ Map* m = ARGM("$map", Map, ctx);
1351
+ List* arglist = ARG("$keys", List);
1352
+ Map* result = new (ctx.mem) Map(path, position, 1);
1353
+ for (auto key : m->keys()) {
1354
+ remove = false;
1355
+ for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) {
1356
+ remove = eq(key, arglist->value_at_index(j), ctx);
1357
+ }
1358
+ if (!remove) *result << make_pair(key, m->at(key));
1359
+ }
1360
+ return result;
1361
+ }
1362
+
1363
+ Signature keywords_sig = "keywords($args)";
1364
+ BUILT_IN(keywords)
1365
+ {
1366
+ List* arglist = new (ctx.mem) List(*ARG("$args", List));
1367
+ Map* result = new (ctx.mem) Map(path, position, 1);
1368
+ // The parser ensures the ordering of arguments so we can assert this
1369
+ // isn't keyword argument list the first argument isn't a keyword argument
1370
+ if (!(arglist->empty() || ((Argument*)(*arglist)[0])->is_keyword_argument())) return result;
1371
+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {
1372
+ string name = string(((Argument*)(*arglist)[i])->name());
1373
+ string sanitized_name = string(name, 1);
1374
+ *result << make_pair(new (ctx.mem) String_Constant(path, position, sanitized_name),
1375
+ ((Argument*)(*arglist)[i])->value());
1376
+ }
1377
+ return result;
1378
+ }
1379
+
1380
+ //////////////////////////
1381
+ // INTROSPECTION FUNCTIONS
1382
+ //////////////////////////
1383
+
1384
+ Signature type_of_sig = "type-of($value)";
1385
+ BUILT_IN(type_of)
1386
+ {
1387
+ Expression* v = ARG("$value", Expression);
1388
+ if (v->concrete_type() == Expression::STRING) {
1389
+ To_String to_string;
1390
+ string str(v->perform(&to_string));
1391
+ if (ctx.names_to_colors.count(str)) {
1392
+ return new (ctx.mem) String_Constant(path, position, "color");
1393
+ }
1394
+ }
1395
+ return new (ctx.mem) String_Constant(path, position, ARG("$value", Expression)->type());
1396
+ }
1397
+
1398
+ Signature unit_sig = "unit($number)";
1399
+ BUILT_IN(unit)
1400
+ { return new (ctx.mem) String_Constant(path, position, quote(ARG("$number", Number)->unit(), '"')); }
1401
+
1402
+ Signature unitless_sig = "unitless($number)";
1403
+ BUILT_IN(unitless)
1404
+ { return new (ctx.mem) Boolean(path, position, ARG("$number", Number)->is_unitless()); }
1405
+
1406
+ Signature comparable_sig = "comparable($number-1, $number-2)";
1407
+ BUILT_IN(comparable)
1408
+ {
1409
+ Number* n1 = ARG("$number-1", Number);
1410
+ Number* n2 = ARG("$number-2", Number);
1411
+ if (n1->is_unitless() || n2->is_unitless()) {
1412
+ return new (ctx.mem) Boolean(path, position, true);
1413
+ }
1414
+ Number tmp_n2(*n2);
1415
+ tmp_n2.normalize(n1->find_convertible_unit());
1416
+ return new (ctx.mem) Boolean(path, position, n1->unit() == tmp_n2.unit());
1417
+ }
1418
+
1419
+ Signature variable_exists_sig = "variable-exists($name)";
1420
+ BUILT_IN(variable_exists)
1421
+ {
1422
+ string s = unquote(ARG("$name", String_Constant)->value());
1423
+
1424
+ if(d_env.has("$"+s)) {
1425
+ return new (ctx.mem) Boolean(path, position, true);
1426
+ }
1427
+ else {
1428
+ return new (ctx.mem) Boolean(path, position, false);
1429
+ }
1430
+ }
1431
+
1432
+ Signature global_variable_exists_sig = "global-variable-exists($name)";
1433
+ BUILT_IN(global_variable_exists)
1434
+ {
1435
+ string s = unquote(ARG("$name", String_Constant)->value());
1436
+
1437
+ if(d_env.global_frame_has("$"+s)) {
1438
+ return new (ctx.mem) Boolean(path, position, true);
1439
+ }
1440
+ else {
1441
+ return new (ctx.mem) Boolean(path, position, false);
1442
+ }
1443
+ }
1444
+
1445
+ Signature function_exists_sig = "function-exists($name)";
1446
+ BUILT_IN(function_exists)
1447
+ {
1448
+ string s = unquote(ARG("$name", String_Constant)->value());
1449
+
1450
+ if(d_env.global_frame_has(s+"[f]")) {
1451
+ return new (ctx.mem) Boolean(path, position, true);
1452
+ }
1453
+ else {
1454
+ return new (ctx.mem) Boolean(path, position, false);
1455
+ }
1456
+ }
1457
+
1458
+ Signature mixin_exists_sig = "mixin-exists($name)";
1459
+ BUILT_IN(mixin_exists)
1460
+ {
1461
+ string s = unquote(ARG("$name", String_Constant)->value());
1462
+
1463
+ if(d_env.global_frame_has(s+"[m]")) {
1464
+ return new (ctx.mem) Boolean(path, position, true);
1465
+ }
1466
+ else {
1467
+ return new (ctx.mem) Boolean(path, position, false);
1468
+ }
1469
+ }
1470
+
1471
+ Signature feature_exists_sig = "feature-exists($name)";
1472
+ BUILT_IN(feature_exists)
1473
+ {
1474
+ string s = unquote(ARG("$name", String_Constant)->value());
1475
+
1476
+ if(features.find(s) == features.end()) {
1477
+ return new (ctx.mem) Boolean(path, position, false);
1478
+ }
1479
+ else {
1480
+ return new (ctx.mem) Boolean(path, position, true);
1481
+ }
1482
+ }
1483
+
1484
+ Signature call_sig = "call($name, $args...)";
1485
+ BUILT_IN(call)
1486
+ {
1487
+ string name = unquote(ARG("$name", String_Constant)->value());
1488
+ List* arglist = new (ctx.mem) List(*ARG("$args", List));
1489
+
1490
+ Arguments* args = new (ctx.mem) Arguments(path, position);
1491
+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {
1492
+ Argument* arg = new (ctx.mem) Argument(path, position, arglist->value_at_index(i));
1493
+ *args << arg;
1494
+ }
1495
+ Function_Call* func = new (ctx.mem) Function_Call(path, position, name, args);
1496
+ Eval eval(ctx, &d_env, backtrace);
1497
+ return func->perform(&eval);
1498
+
1499
+ }
1500
+
1501
+ ////////////////////
1502
+ // BOOLEAN FUNCTIONS
1503
+ ////////////////////
1504
+
1505
+ Signature not_sig = "not($value)";
1506
+ BUILT_IN(sass_not)
1507
+ { return new (ctx.mem) Boolean(path, position, ARG("$value", Expression)->is_false()); }
1508
+
1509
+ Signature if_sig = "if($condition, $if-true, $if-false)";
1510
+ // BUILT_IN(sass_if)
1511
+ // { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); }
1512
+ BUILT_IN(sass_if)
1513
+ {
1514
+ Eval eval(ctx, &d_env, backtrace);
1515
+ bool is_true = !ARG("$condition", Expression)->perform(&eval)->is_false();
1516
+ if (is_true) {
1517
+ return ARG("$if-true", Expression)->perform(&eval);
1518
+ }
1519
+ else {
1520
+ return ARG("$if-false", Expression)->perform(&eval);
1521
+ }
1522
+ }
1523
+
1524
+ ////////////////
1525
+ // URL FUNCTIONS
1526
+ ////////////////
1527
+
1528
+ Signature image_url_sig = "image-url($path, $only-path: false, $cache-buster: false)";
1529
+ BUILT_IN(image_url)
1530
+ {
1531
+ String_Constant* ipath = ARG("$path", String_Constant);
1532
+ bool only_path = !ARG("$only-path", Expression)->is_false();
1533
+ string full_path(quote(ctx.image_path + "/" + unquote(ipath->value()), '"'));
1534
+ if (!only_path) full_path = "url(" + full_path + ")";
1535
+ return new (ctx.mem) String_Constant(path, position, full_path);
1536
+ }
1537
+
1538
+ //////////////////////////
1539
+ // MISCELLANEOUS FUNCTIONS
1540
+ //////////////////////////
1541
+
1542
+ Signature inspect_sig = "inspect($value)";
1543
+ BUILT_IN(inspect)
1544
+ {
1545
+ Expression* v = ARG("$value", Expression);
1546
+ if (v->concrete_type() == Expression::NULL_VAL) {
1547
+ return new (ctx.mem) String_Constant(path, position, "null");
1548
+ } else if (v->concrete_type() == Expression::BOOLEAN && *v == 0) {
1549
+ return new (ctx.mem) String_Constant(path, position, "false");
1550
+ }
1551
+ return v;
1552
+ }
1553
+
1554
+ Signature unique_id_sig = "unique-id()";
1555
+ BUILT_IN(unique_id)
1556
+ {
1557
+ std::stringstream ss;
1558
+ uniform_real_distribution<> distributor(0, 4294967296); // 16^8
1559
+ uint_fast32_t distributed = distributor(rand);
1560
+ ss << "u" << setfill('0') << setw(8) << std::hex << distributed;
1561
+ return new (ctx.mem) String_Constant(path, position, ss.str());
1562
+ }
1563
+
1564
+ }
1565
+ }