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.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.gitmodules +3 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +24 -0
- data/Rakefile +21 -0
- data/ext/libsass/.editorconfig +15 -0
- data/ext/libsass/.gitattributes +2 -0
- data/ext/libsass/.gitignore +61 -0
- data/ext/libsass/.travis.yml +38 -0
- data/ext/libsass/COPYING +25 -0
- data/ext/libsass/INSTALL +1 -0
- data/ext/libsass/LICENSE +25 -0
- data/ext/libsass/Makefile +223 -0
- data/ext/libsass/Makefile.am +145 -0
- data/ext/libsass/Readme.md +93 -0
- data/ext/libsass/appveyor.yml +76 -0
- data/ext/libsass/ast.cpp +581 -0
- data/ext/libsass/ast.hpp +1949 -0
- data/ext/libsass/ast_def_macros.hpp +16 -0
- data/ext/libsass/ast_factory.hpp +87 -0
- data/ext/libsass/ast_fwd_decl.hpp +72 -0
- data/ext/libsass/b64/cencode.h +32 -0
- data/ext/libsass/b64/encode.h +77 -0
- data/ext/libsass/backtrace.hpp +81 -0
- data/ext/libsass/base64vlq.cpp +43 -0
- data/ext/libsass/base64vlq.hpp +28 -0
- data/ext/libsass/bind.cpp +187 -0
- data/ext/libsass/bind.hpp +18 -0
- data/ext/libsass/cencode.c +102 -0
- data/ext/libsass/color_names.hpp +324 -0
- data/ext/libsass/configure.ac +130 -0
- data/ext/libsass/constants.cpp +144 -0
- data/ext/libsass/constants.hpp +145 -0
- data/ext/libsass/context.cpp +507 -0
- data/ext/libsass/context.hpp +150 -0
- data/ext/libsass/contextualize.cpp +157 -0
- data/ext/libsass/contextualize.hpp +65 -0
- data/ext/libsass/copy_c_str.cpp +13 -0
- data/ext/libsass/copy_c_str.hpp +5 -0
- data/ext/libsass/debug.hpp +39 -0
- data/ext/libsass/environment.hpp +75 -0
- data/ext/libsass/error_handling.cpp +28 -0
- data/ext/libsass/error_handling.hpp +28 -0
- data/ext/libsass/eval.cpp +1149 -0
- data/ext/libsass/eval.hpp +80 -0
- data/ext/libsass/expand.cpp +430 -0
- data/ext/libsass/expand.hpp +77 -0
- data/ext/libsass/extconf.rb +6 -0
- data/ext/libsass/extend.cpp +1962 -0
- data/ext/libsass/extend.hpp +50 -0
- data/ext/libsass/file.cpp +291 -0
- data/ext/libsass/file.hpp +18 -0
- data/ext/libsass/functions.cpp +1565 -0
- data/ext/libsass/functions.hpp +187 -0
- data/ext/libsass/inspect.cpp +727 -0
- data/ext/libsass/inspect.hpp +108 -0
- data/ext/libsass/json.cpp +1411 -0
- data/ext/libsass/json.hpp +117 -0
- data/ext/libsass/kwd_arg_macros.hpp +23 -0
- data/ext/libsass/m4/.gitkeep +0 -0
- data/ext/libsass/mapping.hpp +17 -0
- data/ext/libsass/memory_manager.hpp +54 -0
- data/ext/libsass/node.cpp +251 -0
- data/ext/libsass/node.hpp +122 -0
- data/ext/libsass/operation.hpp +153 -0
- data/ext/libsass/output_compressed.cpp +401 -0
- data/ext/libsass/output_compressed.hpp +95 -0
- data/ext/libsass/output_nested.cpp +364 -0
- data/ext/libsass/output_nested.hpp +108 -0
- data/ext/libsass/parser.cpp +2016 -0
- data/ext/libsass/parser.hpp +264 -0
- data/ext/libsass/paths.hpp +69 -0
- data/ext/libsass/position.hpp +22 -0
- data/ext/libsass/posix/getopt.c +562 -0
- data/ext/libsass/posix/getopt.h +95 -0
- data/ext/libsass/prelexer.cpp +688 -0
- data/ext/libsass/prelexer.hpp +513 -0
- data/ext/libsass/remove_placeholders.cpp +59 -0
- data/ext/libsass/remove_placeholders.hpp +43 -0
- data/ext/libsass/res/resource.rc +35 -0
- data/ext/libsass/sass.cpp +33 -0
- data/ext/libsass/sass.h +60 -0
- data/ext/libsass/sass2scss.cpp +834 -0
- data/ext/libsass/sass2scss.h +110 -0
- data/ext/libsass/sass_context.cpp +709 -0
- data/ext/libsass/sass_context.h +120 -0
- data/ext/libsass/sass_functions.cpp +137 -0
- data/ext/libsass/sass_functions.h +90 -0
- data/ext/libsass/sass_interface.cpp +277 -0
- data/ext/libsass/sass_interface.h +97 -0
- data/ext/libsass/sass_util.cpp +136 -0
- data/ext/libsass/sass_util.hpp +259 -0
- data/ext/libsass/sass_values.cpp +337 -0
- data/ext/libsass/sass_values.h +124 -0
- data/ext/libsass/script/bootstrap +10 -0
- data/ext/libsass/script/branding +10 -0
- data/ext/libsass/script/ci-build-libsass +72 -0
- data/ext/libsass/script/ci-install-compiler +4 -0
- data/ext/libsass/script/ci-install-deps +19 -0
- data/ext/libsass/script/ci-report-coverage +25 -0
- data/ext/libsass/script/coveralls-debug +32 -0
- data/ext/libsass/script/spec +5 -0
- data/ext/libsass/script/tap-driver +652 -0
- data/ext/libsass/script/tap-runner +1 -0
- data/ext/libsass/source_map.cpp +133 -0
- data/ext/libsass/source_map.hpp +46 -0
- data/ext/libsass/subset_map.hpp +145 -0
- data/ext/libsass/support/libsass.pc.in +11 -0
- data/ext/libsass/test-driver +127 -0
- data/ext/libsass/test/test_node.cpp +98 -0
- data/ext/libsass/test/test_paths.cpp +29 -0
- data/ext/libsass/test/test_selector_difference.cpp +28 -0
- data/ext/libsass/test/test_specificity.cpp +28 -0
- data/ext/libsass/test/test_subset_map.cpp +472 -0
- data/ext/libsass/test/test_superselector.cpp +71 -0
- data/ext/libsass/test/test_unification.cpp +33 -0
- data/ext/libsass/to_c.cpp +61 -0
- data/ext/libsass/to_c.hpp +44 -0
- data/ext/libsass/to_string.cpp +29 -0
- data/ext/libsass/to_string.hpp +32 -0
- data/ext/libsass/token.hpp +32 -0
- data/ext/libsass/units.cpp +54 -0
- data/ext/libsass/units.hpp +10 -0
- data/ext/libsass/utf8.h +34 -0
- data/ext/libsass/utf8/checked.h +327 -0
- data/ext/libsass/utf8/core.h +329 -0
- data/ext/libsass/utf8/unchecked.h +228 -0
- data/ext/libsass/utf8_string.cpp +102 -0
- data/ext/libsass/utf8_string.hpp +36 -0
- data/ext/libsass/util.cpp +189 -0
- data/ext/libsass/util.hpp +26 -0
- data/ext/libsass/win/libsass.filters +291 -0
- data/ext/libsass/win/libsass.sln +28 -0
- data/ext/libsass/win/libsass.vcxproj +255 -0
- data/lib/sassc.rb +6 -0
- data/lib/sassc/engine.rb +13 -0
- data/lib/sassc/native.rb +44 -0
- data/lib/sassc/native/native_context_api.rb +140 -0
- data/lib/sassc/native/native_functions_api.rb +41 -0
- data/lib/sassc/native/sass_input_style.rb +11 -0
- data/lib/sassc/native/sass_output_style.rb +10 -0
- data/lib/sassc/native/sass_value.rb +95 -0
- data/lib/sassc/native/string_list.rb +8 -0
- data/lib/sassc/version.rb +3 -0
- data/sassc.gemspec +43 -0
- data/test/smoke_test.rb +171 -0
- data/test/test_helper.rb +4 -0
- 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
|
+
}
|