sassc 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +9 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +24 -0
  8. data/Rakefile +21 -0
  9. data/ext/libsass/.editorconfig +15 -0
  10. data/ext/libsass/.gitattributes +2 -0
  11. data/ext/libsass/.gitignore +61 -0
  12. data/ext/libsass/.travis.yml +38 -0
  13. data/ext/libsass/COPYING +25 -0
  14. data/ext/libsass/INSTALL +1 -0
  15. data/ext/libsass/LICENSE +25 -0
  16. data/ext/libsass/Makefile +223 -0
  17. data/ext/libsass/Makefile.am +145 -0
  18. data/ext/libsass/Readme.md +93 -0
  19. data/ext/libsass/appveyor.yml +76 -0
  20. data/ext/libsass/ast.cpp +581 -0
  21. data/ext/libsass/ast.hpp +1949 -0
  22. data/ext/libsass/ast_def_macros.hpp +16 -0
  23. data/ext/libsass/ast_factory.hpp +87 -0
  24. data/ext/libsass/ast_fwd_decl.hpp +72 -0
  25. data/ext/libsass/b64/cencode.h +32 -0
  26. data/ext/libsass/b64/encode.h +77 -0
  27. data/ext/libsass/backtrace.hpp +81 -0
  28. data/ext/libsass/base64vlq.cpp +43 -0
  29. data/ext/libsass/base64vlq.hpp +28 -0
  30. data/ext/libsass/bind.cpp +187 -0
  31. data/ext/libsass/bind.hpp +18 -0
  32. data/ext/libsass/cencode.c +102 -0
  33. data/ext/libsass/color_names.hpp +324 -0
  34. data/ext/libsass/configure.ac +130 -0
  35. data/ext/libsass/constants.cpp +144 -0
  36. data/ext/libsass/constants.hpp +145 -0
  37. data/ext/libsass/context.cpp +507 -0
  38. data/ext/libsass/context.hpp +150 -0
  39. data/ext/libsass/contextualize.cpp +157 -0
  40. data/ext/libsass/contextualize.hpp +65 -0
  41. data/ext/libsass/copy_c_str.cpp +13 -0
  42. data/ext/libsass/copy_c_str.hpp +5 -0
  43. data/ext/libsass/debug.hpp +39 -0
  44. data/ext/libsass/environment.hpp +75 -0
  45. data/ext/libsass/error_handling.cpp +28 -0
  46. data/ext/libsass/error_handling.hpp +28 -0
  47. data/ext/libsass/eval.cpp +1149 -0
  48. data/ext/libsass/eval.hpp +80 -0
  49. data/ext/libsass/expand.cpp +430 -0
  50. data/ext/libsass/expand.hpp +77 -0
  51. data/ext/libsass/extconf.rb +6 -0
  52. data/ext/libsass/extend.cpp +1962 -0
  53. data/ext/libsass/extend.hpp +50 -0
  54. data/ext/libsass/file.cpp +291 -0
  55. data/ext/libsass/file.hpp +18 -0
  56. data/ext/libsass/functions.cpp +1565 -0
  57. data/ext/libsass/functions.hpp +187 -0
  58. data/ext/libsass/inspect.cpp +727 -0
  59. data/ext/libsass/inspect.hpp +108 -0
  60. data/ext/libsass/json.cpp +1411 -0
  61. data/ext/libsass/json.hpp +117 -0
  62. data/ext/libsass/kwd_arg_macros.hpp +23 -0
  63. data/ext/libsass/m4/.gitkeep +0 -0
  64. data/ext/libsass/mapping.hpp +17 -0
  65. data/ext/libsass/memory_manager.hpp +54 -0
  66. data/ext/libsass/node.cpp +251 -0
  67. data/ext/libsass/node.hpp +122 -0
  68. data/ext/libsass/operation.hpp +153 -0
  69. data/ext/libsass/output_compressed.cpp +401 -0
  70. data/ext/libsass/output_compressed.hpp +95 -0
  71. data/ext/libsass/output_nested.cpp +364 -0
  72. data/ext/libsass/output_nested.hpp +108 -0
  73. data/ext/libsass/parser.cpp +2016 -0
  74. data/ext/libsass/parser.hpp +264 -0
  75. data/ext/libsass/paths.hpp +69 -0
  76. data/ext/libsass/position.hpp +22 -0
  77. data/ext/libsass/posix/getopt.c +562 -0
  78. data/ext/libsass/posix/getopt.h +95 -0
  79. data/ext/libsass/prelexer.cpp +688 -0
  80. data/ext/libsass/prelexer.hpp +513 -0
  81. data/ext/libsass/remove_placeholders.cpp +59 -0
  82. data/ext/libsass/remove_placeholders.hpp +43 -0
  83. data/ext/libsass/res/resource.rc +35 -0
  84. data/ext/libsass/sass.cpp +33 -0
  85. data/ext/libsass/sass.h +60 -0
  86. data/ext/libsass/sass2scss.cpp +834 -0
  87. data/ext/libsass/sass2scss.h +110 -0
  88. data/ext/libsass/sass_context.cpp +709 -0
  89. data/ext/libsass/sass_context.h +120 -0
  90. data/ext/libsass/sass_functions.cpp +137 -0
  91. data/ext/libsass/sass_functions.h +90 -0
  92. data/ext/libsass/sass_interface.cpp +277 -0
  93. data/ext/libsass/sass_interface.h +97 -0
  94. data/ext/libsass/sass_util.cpp +136 -0
  95. data/ext/libsass/sass_util.hpp +259 -0
  96. data/ext/libsass/sass_values.cpp +337 -0
  97. data/ext/libsass/sass_values.h +124 -0
  98. data/ext/libsass/script/bootstrap +10 -0
  99. data/ext/libsass/script/branding +10 -0
  100. data/ext/libsass/script/ci-build-libsass +72 -0
  101. data/ext/libsass/script/ci-install-compiler +4 -0
  102. data/ext/libsass/script/ci-install-deps +19 -0
  103. data/ext/libsass/script/ci-report-coverage +25 -0
  104. data/ext/libsass/script/coveralls-debug +32 -0
  105. data/ext/libsass/script/spec +5 -0
  106. data/ext/libsass/script/tap-driver +652 -0
  107. data/ext/libsass/script/tap-runner +1 -0
  108. data/ext/libsass/source_map.cpp +133 -0
  109. data/ext/libsass/source_map.hpp +46 -0
  110. data/ext/libsass/subset_map.hpp +145 -0
  111. data/ext/libsass/support/libsass.pc.in +11 -0
  112. data/ext/libsass/test-driver +127 -0
  113. data/ext/libsass/test/test_node.cpp +98 -0
  114. data/ext/libsass/test/test_paths.cpp +29 -0
  115. data/ext/libsass/test/test_selector_difference.cpp +28 -0
  116. data/ext/libsass/test/test_specificity.cpp +28 -0
  117. data/ext/libsass/test/test_subset_map.cpp +472 -0
  118. data/ext/libsass/test/test_superselector.cpp +71 -0
  119. data/ext/libsass/test/test_unification.cpp +33 -0
  120. data/ext/libsass/to_c.cpp +61 -0
  121. data/ext/libsass/to_c.hpp +44 -0
  122. data/ext/libsass/to_string.cpp +29 -0
  123. data/ext/libsass/to_string.hpp +32 -0
  124. data/ext/libsass/token.hpp +32 -0
  125. data/ext/libsass/units.cpp +54 -0
  126. data/ext/libsass/units.hpp +10 -0
  127. data/ext/libsass/utf8.h +34 -0
  128. data/ext/libsass/utf8/checked.h +327 -0
  129. data/ext/libsass/utf8/core.h +329 -0
  130. data/ext/libsass/utf8/unchecked.h +228 -0
  131. data/ext/libsass/utf8_string.cpp +102 -0
  132. data/ext/libsass/utf8_string.hpp +36 -0
  133. data/ext/libsass/util.cpp +189 -0
  134. data/ext/libsass/util.hpp +26 -0
  135. data/ext/libsass/win/libsass.filters +291 -0
  136. data/ext/libsass/win/libsass.sln +28 -0
  137. data/ext/libsass/win/libsass.vcxproj +255 -0
  138. data/lib/sassc.rb +6 -0
  139. data/lib/sassc/engine.rb +13 -0
  140. data/lib/sassc/native.rb +44 -0
  141. data/lib/sassc/native/native_context_api.rb +140 -0
  142. data/lib/sassc/native/native_functions_api.rb +41 -0
  143. data/lib/sassc/native/sass_input_style.rb +11 -0
  144. data/lib/sassc/native/sass_output_style.rb +10 -0
  145. data/lib/sassc/native/sass_value.rb +95 -0
  146. data/lib/sassc/native/string_list.rb +8 -0
  147. data/lib/sassc/version.rb +3 -0
  148. data/sassc.gemspec +43 -0
  149. data/test/smoke_test.rb +171 -0
  150. data/test/test_helper.rb +4 -0
  151. metadata +281 -0
@@ -0,0 +1,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
+ }