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,28 @@
1
+ #ifndef SASS_ERROR_HANDLING
2
+ #include "error_handling.hpp"
3
+ #endif
4
+
5
+ #include "backtrace.hpp"
6
+ #include "prelexer.hpp"
7
+
8
+ namespace Sass {
9
+
10
+ Sass_Error::Sass_Error(Type type, string path, Position position, string message)
11
+ : type(type), path(path), position(position), message(message)
12
+ { }
13
+
14
+ void error(string msg, string path, Position position)
15
+ { throw Sass_Error(Sass_Error::syntax, path, position, msg); }
16
+
17
+ void error(string msg, string path, Position position, Backtrace* bt)
18
+ {
19
+ if (!path.empty() && Prelexer::string_constant(path.c_str()))
20
+ path = path.substr(1, path.size() - 1);
21
+
22
+ Backtrace top(bt, path, position, "");
23
+ msg += top.to_string();
24
+
25
+ throw Sass_Error(Sass_Error::syntax, path, position, msg);
26
+ }
27
+
28
+ }
@@ -0,0 +1,28 @@
1
+ #define SASS_ERROR_HANDLING
2
+ #include <string>
3
+
4
+ #ifndef SASS_POSITION
5
+ #include "position.hpp"
6
+ #endif
7
+
8
+ namespace Sass {
9
+ using namespace std;
10
+
11
+ struct Backtrace;
12
+
13
+ struct Sass_Error {
14
+ enum Type { read, write, syntax, evaluation };
15
+
16
+ Type type;
17
+ string path;
18
+ Position position;
19
+ string message;
20
+
21
+ Sass_Error(Type type, string path, Position position, string message);
22
+
23
+ };
24
+
25
+ void error(string msg, string path, Position position);
26
+ void error(string msg, string path, Position position, Backtrace* bt);
27
+
28
+ }
@@ -0,0 +1,1149 @@
1
+ #include "file.hpp"
2
+ #include "eval.hpp"
3
+ #include "ast.hpp"
4
+ #include "bind.hpp"
5
+ #include "util.hpp"
6
+ #include "to_string.hpp"
7
+ #include "inspect.hpp"
8
+ #include "to_c.hpp"
9
+ #include "context.hpp"
10
+ #include "backtrace.hpp"
11
+ #include "prelexer.hpp"
12
+
13
+ #include <cstdlib>
14
+ #include <cmath>
15
+ #include <iostream>
16
+ #include <iomanip>
17
+ #include <typeinfo>
18
+
19
+ namespace Sass {
20
+ using namespace std;
21
+
22
+ inline double add(double x, double y) { return x + y; }
23
+ inline double sub(double x, double y) { return x - y; }
24
+ inline double mul(double x, double y) { return x * y; }
25
+ inline double div(double x, double y) { return x / y; } // x/0 checked by caller
26
+ typedef double (*bop)(double, double);
27
+ bop ops[Binary_Expression::NUM_OPS] = {
28
+ 0, 0, // and, or
29
+ 0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte
30
+ add, sub, mul, div, fmod
31
+ };
32
+
33
+ Eval::Eval(Context& ctx, Env* env, Backtrace* bt)
34
+ : ctx(ctx), env(env), backtrace(bt) { }
35
+ Eval::~Eval() { }
36
+
37
+ Eval* Eval::with(Env* e, Backtrace* bt) // for setting the env before eval'ing an expression
38
+ {
39
+ env = e;
40
+ backtrace = bt;
41
+ return this;
42
+ }
43
+
44
+ Expression* Eval::operator()(Block* b)
45
+ {
46
+ Expression* val = 0;
47
+ for (size_t i = 0, L = b->length(); i < L; ++i) {
48
+ val = (*b)[i]->perform(this);
49
+ if (val) return val;
50
+ }
51
+ return val;
52
+ }
53
+
54
+ Expression* Eval::operator()(Assignment* a)
55
+ {
56
+ string var(a->variable());
57
+ if (env->has(var)) {
58
+ Expression* v = static_cast<Expression*>((*env)[var]);
59
+ if (!a->is_guarded() || v->concrete_type() == Expression::NULL_VAL) (*env)[var] = a->value()->perform(this);
60
+ }
61
+ else {
62
+ env->current_frame()[var] = a->value()->perform(this);
63
+ }
64
+ return 0;
65
+ }
66
+
67
+ Expression* Eval::operator()(If* i)
68
+ {
69
+ if (*i->predicate()->perform(this)) {
70
+ return i->consequent()->perform(this);
71
+ }
72
+ else {
73
+ Block* alt = i->alternative();
74
+ if (alt) return alt->perform(this);
75
+ }
76
+ return 0;
77
+ }
78
+
79
+ Expression* Eval::operator()(For* f)
80
+ {
81
+ string variable(f->variable());
82
+ Expression* low = f->lower_bound()->perform(this);
83
+ if (low->concrete_type() != Expression::NUMBER) {
84
+ error("lower bound of `@for` directive must be numeric", low->path(), low->position());
85
+ }
86
+ Expression* high = f->upper_bound()->perform(this);
87
+ if (high->concrete_type() != Expression::NUMBER) {
88
+ error("upper bound of `@for` directive must be numeric", high->path(), high->position());
89
+ }
90
+ double start = static_cast<Number*>(low)->value();
91
+ double end = static_cast<Number*>(high)->value();
92
+ Env new_env;
93
+ new_env[variable] = new (ctx.mem) Number(low->path(), low->position(), start);
94
+ new_env.link(env);
95
+ env = &new_env;
96
+ Block* body = f->block();
97
+ Expression* val = 0;
98
+ if (start < end) {
99
+ if (f->is_inclusive()) ++end;
100
+ for (double i = start;
101
+ i < end;
102
+ (*env)[variable] = new (ctx.mem) Number(low->path(), low->position(), ++i)) {
103
+ val = body->perform(this);
104
+ if (val) break;
105
+ }
106
+ } else {
107
+ if (f->is_inclusive()) --end;
108
+ for (double i = start;
109
+ i > end;
110
+ (*env)[variable] = new (ctx.mem) Number(low->path(), low->position(), --i)) {
111
+ val = body->perform(this);
112
+ if (val) break;
113
+ }
114
+ }
115
+ env = new_env.parent();
116
+ return val;
117
+ }
118
+
119
+ Expression* Eval::operator()(Each* e)
120
+ {
121
+ vector<string> variables(e->variables());
122
+ Expression* expr = e->list()->perform(this);
123
+ List* list = 0;
124
+ Map* map = 0;
125
+ if (expr->concrete_type() == Expression::MAP) {
126
+ map = static_cast<Map*>(expr);
127
+ }
128
+ else if (expr->concrete_type() != Expression::LIST) {
129
+ list = new (ctx.mem) List(expr->path(), expr->position(), 1, List::COMMA);
130
+ *list << expr;
131
+ }
132
+ else {
133
+ list = static_cast<List*>(expr);
134
+ }
135
+ Env new_env;
136
+ for (size_t i = 0, L = variables.size(); i < L; ++i) new_env[variables[i]] = 0;
137
+ new_env.link(env);
138
+ env = &new_env;
139
+ Block* body = e->block();
140
+ Expression* val = 0;
141
+
142
+ if (map) {
143
+ for (auto key : map->keys()) {
144
+ Expression* value = map->at(key);
145
+
146
+ if (variables.size() == 1) {
147
+ List* variable = new (ctx.mem) List(map->path(), map->position(), 2, List::SPACE);
148
+ *variable << key;
149
+ *variable << value;
150
+ (*env)[variables[0]] = variable;
151
+ } else {
152
+ (*env)[variables[0]] = key;
153
+ (*env)[variables[1]] = value;
154
+ }
155
+
156
+ val = body->perform(this);
157
+ if (val) break;
158
+ }
159
+ }
160
+ else {
161
+ for (size_t i = 0, L = list->length(); i < L; ++i) {
162
+ List* variable = 0;
163
+ if ((*list)[i]->concrete_type() != Expression::LIST || variables.size() == 1) {
164
+ variable = new (ctx.mem) List((*list)[i]->path(), (*list)[i]->position(), 1, List::COMMA);
165
+ *variable << (*list)[i];
166
+ }
167
+ else {
168
+ variable = static_cast<List*>((*list)[i]);
169
+ }
170
+ for (size_t j = 0, K = variables.size(); j < K; ++j) {
171
+ if (j < variable->length()) {
172
+ (*env)[variables[j]] = (*variable)[j];
173
+ }
174
+ else {
175
+ (*env)[variables[j]] = new (ctx.mem) Null(expr->path(), expr->position());
176
+ }
177
+ val = body->perform(this);
178
+ if (val) break;
179
+ }
180
+ if (val) break;
181
+ }
182
+ }
183
+ env = new_env.parent();
184
+ return val;
185
+ }
186
+
187
+ Expression* Eval::operator()(While* w)
188
+ {
189
+ Expression* pred = w->predicate();
190
+ Block* body = w->block();
191
+ while (*pred->perform(this)) {
192
+ Expression* val = body->perform(this);
193
+ if (val) return val;
194
+ }
195
+ return 0;
196
+ }
197
+
198
+ Expression* Eval::operator()(Return* r)
199
+ {
200
+ return r->value()->perform(this);
201
+ }
202
+
203
+ Expression* Eval::operator()(Warning* w)
204
+ {
205
+ Expression* message = w->message()->perform(this);
206
+ To_String to_string;
207
+
208
+ // try to use generic function
209
+ if (env->has("@warn[f]")) {
210
+
211
+ Definition* def = static_cast<Definition*>((*env)["@warn[f]"]);
212
+ // Block* body = def->block();
213
+ // Native_Function func = def->native_function();
214
+ Sass_C_Function c_func = def->c_function();
215
+
216
+ To_C to_c;
217
+ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);
218
+ sass_list_set_value(c_args, 0, message->perform(&to_c));
219
+ Sass_Value* c_val = c_func(c_args, def->cookie());
220
+ sass_delete_value(c_args);
221
+ sass_delete_value(c_val);
222
+ return 0;
223
+
224
+ }
225
+
226
+ string result(unquote(message->perform(&to_string)));
227
+ Backtrace top(backtrace, w->path(), w->position(), "");
228
+ cerr << "WARNING: " << result;
229
+ cerr << top.to_string(true);
230
+ cerr << endl << endl;
231
+ return 0;
232
+ }
233
+
234
+ Expression* Eval::operator()(Error* e)
235
+ {
236
+ Expression* message = e->message()->perform(this);
237
+ To_String to_string;
238
+
239
+ // try to use generic function
240
+ if (env->has("@error[f]")) {
241
+
242
+ Definition* def = static_cast<Definition*>((*env)["@error[f]"]);
243
+ // Block* body = def->block();
244
+ // Native_Function func = def->native_function();
245
+ Sass_C_Function c_func = def->c_function();
246
+
247
+ To_C to_c;
248
+ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);
249
+ sass_list_set_value(c_args, 0, message->perform(&to_c));
250
+ Sass_Value* c_val = c_func(c_args, def->cookie());
251
+ sass_delete_value(c_args);
252
+ sass_delete_value(c_val);
253
+ return 0;
254
+
255
+ }
256
+
257
+ string result(unquote(message->perform(&to_string)));
258
+ Backtrace top(backtrace, e->path(), e->position(), "");
259
+ cerr << "Error: " << result;
260
+ cerr << top.to_string(true);
261
+ cerr << endl << endl;
262
+ return 0;
263
+ }
264
+
265
+ Expression* Eval::operator()(Debug* d)
266
+ {
267
+ Expression* message = d->value()->perform(this);
268
+ To_String to_string;
269
+
270
+ // try to use generic function
271
+ if (env->has("@debug[f]")) {
272
+
273
+ Definition* def = static_cast<Definition*>((*env)["@debug[f]"]);
274
+ // Block* body = def->block();
275
+ // Native_Function func = def->native_function();
276
+ Sass_C_Function c_func = def->c_function();
277
+
278
+ To_C to_c;
279
+ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);
280
+ sass_list_set_value(c_args, 0, message->perform(&to_c));
281
+ Sass_Value* c_val = c_func(c_args, def->cookie());
282
+ sass_delete_value(c_args);
283
+ sass_delete_value(c_val);
284
+ return 0;
285
+
286
+ }
287
+
288
+ string cwd(ctx.get_cwd());
289
+ string result(unquote(message->perform(&to_string)));
290
+ string rel_path(Sass::File::resolve_relative_path(d->path(), cwd, cwd));
291
+ cerr << rel_path << ":" << d->position().line << ":" << " DEBUG: " << result;
292
+ cerr << endl;
293
+ return 0;
294
+ }
295
+
296
+ Expression* Eval::operator()(List* l)
297
+ {
298
+ if (l->is_expanded()) return l;
299
+ List* ll = new (ctx.mem) List(l->path(),
300
+ l->position(),
301
+ l->length(),
302
+ l->separator(),
303
+ l->is_arglist());
304
+ for (size_t i = 0, L = l->length(); i < L; ++i) {
305
+ *ll << (*l)[i]->perform(this);
306
+ }
307
+ ll->is_expanded(true);
308
+ return ll;
309
+ }
310
+
311
+ Expression* Eval::operator()(Map* m)
312
+ {
313
+ if (m->is_expanded()) return m;
314
+ Map* mm = new (ctx.mem) Map(m->path(),
315
+ m->position(),
316
+ m->length());
317
+ for (auto key : m->keys()) {
318
+ *mm << std::make_pair(key->perform(this), m->at(key)->perform(this));
319
+ }
320
+ mm->is_expanded(true);
321
+ return mm;
322
+ }
323
+
324
+ // -- only need to define two comparisons, and the rest can be implemented in terms of them
325
+ bool eq(Expression*, Expression*, Context&, Eval*);
326
+ bool lt(Expression*, Expression*, Context&);
327
+ // -- arithmetic on the combinations that matter
328
+ Expression* op_numbers(Context&, Binary_Expression*, Expression*, Expression*);
329
+ Expression* op_number_color(Context&, Binary_Expression::Type, Expression*, Expression*);
330
+ Expression* op_color_number(Context&, Binary_Expression::Type, Expression*, Expression*);
331
+ Expression* op_colors(Context&, Binary_Expression::Type, Expression*, Expression*);
332
+ Expression* op_strings(Context&, Binary_Expression::Type, Expression*, Expression*);
333
+
334
+ Expression* Eval::operator()(Binary_Expression* b)
335
+ {
336
+ Binary_Expression::Type op_type = b->type();
337
+ // don't eval delayed expressions (the '/' when used as a separator)
338
+ if (op_type == Binary_Expression::DIV && b->is_delayed()) return b;
339
+ // if one of the operands is a '/' then make sure it's evaluated
340
+ if (typeid(*b->left()) == typeid(Binary_Expression)) b->left()->is_delayed(false);
341
+ // the logical connectives need to short-circuit
342
+ Expression* lhs = b->left()->perform(this);
343
+ switch (op_type) {
344
+ case Binary_Expression::AND:
345
+ return *lhs ? b->right()->perform(this) : lhs;
346
+ break;
347
+
348
+ case Binary_Expression::OR:
349
+ return *lhs ? lhs : b->right()->perform(this);
350
+ break;
351
+
352
+ default:
353
+ break;
354
+ }
355
+ // not a logical connective, so go ahead and eval the rhs
356
+ Expression* rhs = b->right()->perform(this);
357
+
358
+ // see if it's a relational expression
359
+ switch(op_type) {
360
+ case Binary_Expression::EQ: return new (ctx.mem) Boolean(b->path(), b->position(), eq(lhs, rhs, ctx));
361
+ case Binary_Expression::NEQ: return new (ctx.mem) Boolean(b->path(), b->position(), !eq(lhs, rhs, ctx));
362
+ case Binary_Expression::GT: return new (ctx.mem) Boolean(b->path(), b->position(), !lt(lhs, rhs, ctx) && !eq(lhs, rhs, ctx));
363
+ case Binary_Expression::GTE: return new (ctx.mem) Boolean(b->path(), b->position(), !lt(lhs, rhs, ctx));
364
+ case Binary_Expression::LT: return new (ctx.mem) Boolean(b->path(), b->position(), lt(lhs, rhs, ctx));
365
+ case Binary_Expression::LTE: return new (ctx.mem) Boolean(b->path(), b->position(), lt(lhs, rhs, ctx) || eq(lhs, rhs, ctx));
366
+
367
+ default: break;
368
+ }
369
+
370
+ Expression::Concrete_Type l_type = lhs->concrete_type();
371
+ Expression::Concrete_Type r_type = rhs->concrete_type();
372
+
373
+ if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {
374
+ return op_numbers(ctx, b, lhs, rhs);
375
+ }
376
+ if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {
377
+ return op_number_color(ctx, op_type, lhs, rhs);
378
+ }
379
+ if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {
380
+ return op_color_number(ctx, op_type, lhs, rhs);
381
+ }
382
+ if (l_type == Expression::COLOR && r_type == Expression::COLOR) {
383
+ return op_colors(ctx, op_type, lhs, rhs);
384
+ }
385
+ return op_strings(ctx, op_type, lhs, rhs);
386
+ }
387
+
388
+ Expression* Eval::operator()(Unary_Expression* u)
389
+ {
390
+ Expression* operand = u->operand()->perform(this);
391
+ if (u->type() == Unary_Expression::NOT) {
392
+ Boolean* result = new (ctx.mem) Boolean(u->path(), u->position(), (bool)*operand);
393
+ result->value(!result->value());
394
+ return result;
395
+ }
396
+ else if (operand->concrete_type() == Expression::NUMBER) {
397
+ Number* result = new (ctx.mem) Number(*static_cast<Number*>(operand));
398
+ result->value(u->type() == Unary_Expression::MINUS
399
+ ? -result->value()
400
+ : result->value());
401
+ return result;
402
+ }
403
+ else {
404
+ To_String to_string;
405
+ // Special cases: +/- variables which evaluate to null ouput just +/-,
406
+ // but +/- null itself outputs the string
407
+ if (operand->concrete_type() == Expression::NULL_VAL && typeid(*(u->operand())) == typeid(Variable)) {
408
+ u->operand(new (ctx.mem) String_Constant(u->path(), u->position(), ""));
409
+ }
410
+ else u->operand(operand);
411
+ String_Constant* result = new (ctx.mem) String_Constant(u->path(),
412
+ u->position(),
413
+ u->perform(&to_string));
414
+ return result;
415
+ }
416
+ // unreachable
417
+ return u;
418
+ }
419
+
420
+ Expression* Eval::operator()(Function_Call* c)
421
+ {
422
+ string full_name(c->name() + "[f]");
423
+ Arguments* args = c->arguments();
424
+ if (full_name != "if[f]") {
425
+ args = static_cast<Arguments*>(args->perform(this));
426
+ }
427
+
428
+ // try to use generic function
429
+ if (!env->has(full_name)) {
430
+ if (env->has("*[f]")) {
431
+ full_name = "*[f]";
432
+ }
433
+ }
434
+
435
+ // if it doesn't exist, just pass it through as a literal
436
+ if (!env->has(full_name)) {
437
+ Function_Call* lit = new (ctx.mem) Function_Call(c->path(),
438
+ c->position(),
439
+ c->name(),
440
+ args);
441
+ To_String to_string;
442
+ return new (ctx.mem) String_Constant(c->path(),
443
+ c->position(),
444
+ lit->perform(&to_string));
445
+ }
446
+
447
+ Expression* result = c;
448
+ Definition* def = static_cast<Definition*>((*env)[full_name]);
449
+ Block* body = def->block();
450
+ Native_Function func = def->native_function();
451
+ Sass_C_Function c_func = def->c_function();
452
+
453
+ if (full_name != "if[f]") {
454
+ for (size_t i = 0, L = args->length(); i < L; ++i) {
455
+ (*args)[i]->value((*args)[i]->value()->perform(this));
456
+ }
457
+ }
458
+
459
+ Parameters* params = def->parameters();
460
+ Env new_env;
461
+ new_env.link(def->environment());
462
+ // bind("function " + c->name(), params, args, ctx, &new_env, this);
463
+ // Env* old_env = env;
464
+ // env = &new_env;
465
+
466
+ // Backtrace here(backtrace, c->path(), c->line(), ", in function `" + c->name() + "`");
467
+ // backtrace = &here;
468
+
469
+ // if it's user-defined, eval the body
470
+ if (body) {
471
+
472
+ bind("function " + c->name(), params, args, ctx, &new_env, this);
473
+ Env* old_env = env;
474
+ env = &new_env;
475
+
476
+ Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`");
477
+ backtrace = &here;
478
+
479
+ result = body->perform(this);
480
+ if (!result) {
481
+ error(string("function ") + c->name() + " did not return a value", c->path(), c->position());
482
+ }
483
+ backtrace = here.parent;
484
+ env = old_env;
485
+ }
486
+ // if it's native, invoke the underlying CPP function
487
+ else if (func) {
488
+
489
+ bind("function " + c->name(), params, args, ctx, &new_env, this);
490
+ Env* old_env = env;
491
+ env = &new_env;
492
+
493
+ Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`");
494
+ backtrace = &here;
495
+
496
+ result = func(*env, *old_env, ctx, def->signature(), c->path(), c->position(), backtrace);
497
+
498
+ backtrace = here.parent;
499
+ env = old_env;
500
+ }
501
+ // else if it's a user-defined c function
502
+ else if (c_func) {
503
+
504
+ if (full_name == "*[f]") {
505
+ String_Constant *str = new (ctx.mem) String_Constant(c->path(), c->position(), c->name());
506
+ Arguments* new_args = new (ctx.mem) Arguments(c->path(), c->position());
507
+ *new_args << new (ctx.mem) Argument(c->path(), c->position(), str);
508
+ *new_args += args;
509
+ args = new_args;
510
+ }
511
+
512
+ // populates env with default values for params
513
+ bind("function " + c->name(), params, args, ctx, &new_env, this);
514
+ Env* old_env = env;
515
+ env = &new_env;
516
+
517
+ Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`");
518
+ backtrace = &here;
519
+
520
+ To_C to_c;
521
+ union Sass_Value* c_args = sass_make_list(env->current_frame().size(), SASS_COMMA);
522
+ for(size_t i = 0; i < params[0].length(); i++) {
523
+ string key = params[0][i]->name();
524
+ AST_Node* node = env->current_frame().at(key);
525
+ Expression* arg = static_cast<Expression*>(node);
526
+ sass_list_set_value(c_args, i, arg->perform(&to_c));
527
+ }
528
+ Sass_Value* c_val = c_func(c_args, def->cookie());
529
+ if (sass_value_get_tag(c_val) == SASS_ERROR) {
530
+ error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->path(), c->position(), backtrace);
531
+ } else if (sass_value_get_tag(c_val) == SASS_WARNING) {
532
+ error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->path(), c->position(), backtrace);
533
+ }
534
+ result = cval_to_astnode(c_val, ctx, backtrace, c->path(), c->position());
535
+
536
+ backtrace = here.parent;
537
+ sass_delete_value(c_args);
538
+ if (c_val != c_args)
539
+ sass_delete_value(c_val);
540
+ env = old_env;
541
+ }
542
+ // else it's an overloaded native function; resolve it
543
+ else if (def->is_overload_stub()) {
544
+ size_t arity = args->length();
545
+ stringstream ss;
546
+ ss << full_name << arity;
547
+ string resolved_name(ss.str());
548
+ if (!env->has(resolved_name)) error("overloaded function `" + string(c->name()) + "` given wrong number of arguments", c->path(), c->position());
549
+ Definition* resolved_def = static_cast<Definition*>((*env)[resolved_name]);
550
+ params = resolved_def->parameters();
551
+ Env newer_env;
552
+ newer_env.link(resolved_def->environment());
553
+ bind("function " + c->name(), params, args, ctx, &newer_env, this);
554
+ Env* old_env = env;
555
+ env = &newer_env;
556
+
557
+ Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`");
558
+ backtrace = &here;
559
+
560
+ result = resolved_def->native_function()(*env, *old_env, ctx, resolved_def->signature(), c->path(), c->position(), backtrace);
561
+
562
+ backtrace = here.parent;
563
+ env = old_env;
564
+ }
565
+
566
+ // backtrace = here.parent;
567
+ // env = old_env;
568
+ result->position(c->position());
569
+ return result;
570
+ }
571
+
572
+ Expression* Eval::operator()(Function_Call_Schema* s)
573
+ {
574
+ Expression* evaluated_name = s->name()->perform(this);
575
+ Expression* evaluated_args = s->arguments()->perform(this);
576
+ String_Schema* ss = new (ctx.mem) String_Schema(s->path(), s->position(), 2);
577
+ (*ss) << evaluated_name << evaluated_args;
578
+ return ss->perform(this);
579
+ }
580
+
581
+ Expression* Eval::operator()(Variable* v)
582
+ {
583
+ To_String to_string;
584
+ string name(v->name());
585
+ Expression* value = 0;
586
+ if (env->has(name)) value = static_cast<Expression*>((*env)[name]);
587
+ else error("unbound variable " + v->name(), v->path(), v->position());
588
+ // cerr << "name: " << v->name() << "; type: " << typeid(*value).name() << "; value: " << value->perform(&to_string) << endl;
589
+ if (typeid(*value) == typeid(Argument)) value = static_cast<Argument*>(value)->value();
590
+
591
+ // behave according to as ruby sass (add leading zero)
592
+ if (value->concrete_type() == Expression::NUMBER) {
593
+ Number* n = static_cast<Number*>(value);
594
+ value = new (ctx.mem) Number(n->path(),
595
+ n->position(),
596
+ n->value(),
597
+ n->unit(),
598
+ true);
599
+ }
600
+ else if (value->concrete_type() == Expression::STRING) {
601
+ String_Constant* s = static_cast<String_Constant*>(value);
602
+ value = new (ctx.mem) String_Constant(s->path(),
603
+ s->position(),
604
+ s->value());
605
+ }
606
+
607
+ // cerr << "\ttype is now: " << typeid(*value).name() << endl << endl;
608
+ return value;
609
+ }
610
+
611
+ Expression* Eval::operator()(Textual* t)
612
+ {
613
+ using Prelexer::number;
614
+ Expression* result = 0;
615
+ bool zero = !( t->value().substr(0, 1) == "." ||
616
+ t->value().substr(0, 2) == "-." );
617
+
618
+ const string& text = t->value();
619
+ size_t num_pos = text.find_first_not_of(" \n\r\t");
620
+ if (num_pos == string::npos) num_pos = text.length();
621
+ size_t unit_pos = text.find_first_not_of("-+0123456789.", num_pos);
622
+ if (unit_pos == string::npos) unit_pos = text.length();
623
+ const string& num = text.substr(num_pos, unit_pos - num_pos);
624
+
625
+ switch (t->type())
626
+ {
627
+ case Textual::NUMBER:
628
+ result = new (ctx.mem) Number(t->path(),
629
+ t->position(),
630
+ atof(num.c_str()),
631
+ "",
632
+ zero);
633
+ break;
634
+ case Textual::PERCENTAGE:
635
+ result = new (ctx.mem) Number(t->path(),
636
+ t->position(),
637
+ atof(num.c_str()),
638
+ "%",
639
+ zero);
640
+ break;
641
+ case Textual::DIMENSION:
642
+ result = new (ctx.mem) Number(t->path(),
643
+ t->position(),
644
+ atof(num.c_str()),
645
+ Token(number(text.c_str())),
646
+ zero);
647
+ break;
648
+ case Textual::HEX: {
649
+ string hext(t->value().substr(1)); // chop off the '#'
650
+ if (hext.length() == 6) {
651
+ string r(hext.substr(0,2));
652
+ string g(hext.substr(2,2));
653
+ string b(hext.substr(4,2));
654
+ result = new (ctx.mem) Color(t->path(),
655
+ t->position(),
656
+ static_cast<double>(strtol(r.c_str(), NULL, 16)),
657
+ static_cast<double>(strtol(g.c_str(), NULL, 16)),
658
+ static_cast<double>(strtol(b.c_str(), NULL, 16)),
659
+ 1, true,
660
+ t->value());
661
+ }
662
+ else {
663
+ result = new (ctx.mem) Color(t->path(),
664
+ t->position(),
665
+ static_cast<double>(strtol(string(2,hext[0]).c_str(), NULL, 16)),
666
+ static_cast<double>(strtol(string(2,hext[1]).c_str(), NULL, 16)),
667
+ static_cast<double>(strtol(string(2,hext[2]).c_str(), NULL, 16)),
668
+ 1, false,
669
+ t->value());
670
+ }
671
+ } break;
672
+ }
673
+ return result;
674
+ }
675
+
676
+ Expression* Eval::operator()(Number* n)
677
+ {
678
+ // behave according to as ruby sass (add leading zero)
679
+ return new (ctx.mem) Number(n->path(),
680
+ n->position(),
681
+ n->value(),
682
+ n->unit(),
683
+ true);
684
+ }
685
+
686
+ Expression* Eval::operator()(Boolean* b)
687
+ {
688
+ return b;
689
+ }
690
+
691
+ char is_quoted(string str)
692
+ {
693
+ size_t len = str.length();
694
+ if (len < 2) return 0;
695
+ if ((str[0] == '"' && str[len-1] == '"') || (str[0] == '\'' && str[len-1] == '\'')) {
696
+ return str[0];
697
+ }
698
+ else {
699
+ return 0;
700
+ }
701
+ }
702
+
703
+ Expression* Eval::operator()(String_Schema* s)
704
+ {
705
+ string acc;
706
+ ctx._skip_source_map_update = true;
707
+ To_String to_string(&ctx);
708
+ ctx._skip_source_map_update = false;
709
+ for (size_t i = 0, L = s->length(); i < L; ++i) {
710
+ string chunk((*s)[i]->perform(this)->perform(&to_string));
711
+ if (((s->quote_mark() && is_quoted(chunk)) || !s->quote_mark()) && (*s)[i]->is_interpolant()) { // some redundancy in that test
712
+ acc += unquote(chunk);
713
+ }
714
+ else {
715
+ acc += chunk;
716
+ }
717
+ }
718
+ return new (ctx.mem) String_Constant(s->path(),
719
+ s->position(),
720
+ acc);
721
+ }
722
+
723
+ Expression* Eval::operator()(String_Constant* s)
724
+ {
725
+ if (!s->is_delayed() && ctx.names_to_colors.count(s->value())) {
726
+ Color* c = new (ctx.mem) Color(*ctx.names_to_colors[s->value()]);
727
+ c->path(s->path());
728
+ c->position(s->position());
729
+ c->disp(s->value());
730
+ return c;
731
+ }
732
+ return s;
733
+ }
734
+
735
+ Expression* Eval::operator()(Feature_Query* q)
736
+ {
737
+ Feature_Query* qq = new (ctx.mem) Feature_Query(q->path(),
738
+ q->position(),
739
+ q->length());
740
+ for (size_t i = 0, L = q->length(); i < L; ++i) {
741
+ *qq << static_cast<Feature_Query_Condition*>((*q)[i]->perform(this));
742
+ }
743
+ return qq;
744
+ }
745
+
746
+ Expression* Eval::operator()(Feature_Query_Condition* c)
747
+ {
748
+ String* feature = c->feature();
749
+ Expression* value = c->value();
750
+ value = (value ? value->perform(this) : 0);
751
+ Feature_Query_Condition* cc = new (ctx.mem) Feature_Query_Condition(c->path(),
752
+ c->position(),
753
+ c->length(),
754
+ feature,
755
+ value,
756
+ c->operand(),
757
+ c->is_root());
758
+ for (size_t i = 0, L = c->length(); i < L; ++i) {
759
+ *cc << static_cast<Feature_Query_Condition*>((*c)[i]->perform(this));
760
+ }
761
+ return cc;
762
+ }
763
+
764
+ Expression* Eval::operator()(Media_Query* q)
765
+ {
766
+ String* t = q->media_type();
767
+ t = static_cast<String*>(t ? t->perform(this) : 0);
768
+ Media_Query* qq = new (ctx.mem) Media_Query(q->path(),
769
+ q->position(),
770
+ t,
771
+ q->length(),
772
+ q->is_negated(),
773
+ q->is_restricted());
774
+ for (size_t i = 0, L = q->length(); i < L; ++i) {
775
+ *qq << static_cast<Media_Query_Expression*>((*q)[i]->perform(this));
776
+ }
777
+ return qq;
778
+ }
779
+
780
+ Expression* Eval::operator()(Media_Query_Expression* e)
781
+ {
782
+ Expression* feature = e->feature();
783
+ feature = (feature ? feature->perform(this) : 0);
784
+ Expression* value = e->value();
785
+ value = (value ? value->perform(this) : 0);
786
+ return new (ctx.mem) Media_Query_Expression(e->path(),
787
+ e->position(),
788
+ feature,
789
+ value,
790
+ e->is_interpolated());
791
+ }
792
+
793
+ Expression* Eval::operator()(Null* n)
794
+ {
795
+ return n;
796
+ }
797
+
798
+ Expression* Eval::operator()(Argument* a)
799
+ {
800
+ Expression* val = a->value();
801
+ val->is_delayed(false);
802
+ val = val->perform(this);
803
+ val->is_delayed(false);
804
+
805
+ bool is_rest_argument = a->is_rest_argument();
806
+ bool is_keyword_argument = a->is_keyword_argument();
807
+
808
+ if (a->is_rest_argument()) {
809
+ if (val->concrete_type() == Expression::MAP) {
810
+ is_rest_argument = false;
811
+ is_keyword_argument = true;
812
+ }
813
+ else
814
+ if(val->concrete_type() != Expression::LIST) {
815
+ List* wrapper = new (ctx.mem) List(val->path(),
816
+ val->position(),
817
+ 0,
818
+ List::COMMA,
819
+ true);
820
+ *wrapper << val;
821
+ val = wrapper;
822
+ }
823
+ }
824
+ return new (ctx.mem) Argument(a->path(),
825
+ a->position(),
826
+ val,
827
+ a->name(),
828
+ is_rest_argument,
829
+ is_keyword_argument);
830
+ }
831
+
832
+ Expression* Eval::operator()(Arguments* a)
833
+ {
834
+ Arguments* aa = new (ctx.mem) Arguments(a->path(), a->position());
835
+ for (size_t i = 0, L = a->length(); i < L; ++i) {
836
+ *aa << static_cast<Argument*>((*a)[i]->perform(this));
837
+ }
838
+ return aa;
839
+ }
840
+
841
+ inline Expression* Eval::fallback_impl(AST_Node* n)
842
+ {
843
+ return static_cast<Expression*>(n);
844
+ }
845
+
846
+ // All the binary helpers.
847
+
848
+ bool eq(Expression* lhs, Expression* rhs, Context& ctx)
849
+ {
850
+ Expression::Concrete_Type ltype = lhs->concrete_type();
851
+ Expression::Concrete_Type rtype = rhs->concrete_type();
852
+ if (ltype != rtype) return false;
853
+ switch (ltype) {
854
+
855
+ case Expression::BOOLEAN: {
856
+ return static_cast<Boolean*>(lhs)->value() ==
857
+ static_cast<Boolean*>(rhs)->value();
858
+ } break;
859
+
860
+ case Expression::NUMBER: {
861
+ Number* l = static_cast<Number*>(lhs);
862
+ Number* r = static_cast<Number*>(rhs);
863
+ Number tmp_r(*r);
864
+ tmp_r.normalize(l->find_convertible_unit());
865
+ return l->unit() == tmp_r.unit() && l->value() == tmp_r.value()
866
+ ? true
867
+ : false;
868
+ } break;
869
+
870
+ case Expression::COLOR: {
871
+ Color* l = static_cast<Color*>(lhs);
872
+ Color* r = static_cast<Color*>(rhs);
873
+ return l->r() == r->r() &&
874
+ l->g() == r->g() &&
875
+ l->b() == r->b() &&
876
+ l->a() == r->a();
877
+ } break;
878
+
879
+ case Expression::STRING: {
880
+ return unquote(static_cast<String_Constant*>(lhs)->value()) ==
881
+ unquote(static_cast<String_Constant*>(rhs)->value());
882
+ } break;
883
+
884
+ case Expression::LIST: {
885
+ List* l = static_cast<List*>(lhs);
886
+ List* r = static_cast<List*>(rhs);
887
+ if (l->length() != r->length()) return false;
888
+ if (l->separator() != r->separator()) return false;
889
+ for (size_t i = 0, L = l->length(); i < L; ++i) {
890
+ if (!eq((*l)[i], (*r)[i], ctx)) return false;
891
+ }
892
+ return true;
893
+ } break;
894
+
895
+ case Expression::MAP: {
896
+ Map* l = static_cast<Map*>(lhs);
897
+ Map* r = static_cast<Map*>(rhs);
898
+ if (l->length() != r->length()) return false;
899
+ for (auto key : l->keys())
900
+ if (!eq(l->at(key), r->at(key), ctx)) return false;
901
+ return true;
902
+ } break;
903
+ case Expression::NULL_VAL: {
904
+ return true;
905
+ } break;
906
+
907
+ default: break;
908
+ }
909
+ return false;
910
+ }
911
+
912
+ bool lt(Expression* lhs, Expression* rhs, Context& ctx)
913
+ {
914
+ if (lhs->concrete_type() != Expression::NUMBER ||
915
+ rhs->concrete_type() != Expression::NUMBER)
916
+ error("may only compare numbers", lhs->path(), lhs->position());
917
+ Number* l = static_cast<Number*>(lhs);
918
+ Number* r = static_cast<Number*>(rhs);
919
+ Number tmp_r(*r);
920
+ tmp_r.normalize(l->find_convertible_unit());
921
+ string l_unit(l->unit());
922
+ string r_unit(tmp_r.unit());
923
+ if (!l_unit.empty() && !r_unit.empty() && l->unit() != tmp_r.unit()) {
924
+ error("cannot compare numbers with incompatible units", l->path(), l->position());
925
+ }
926
+ return l->value() < tmp_r.value();
927
+ }
928
+
929
+ Expression* op_numbers(Context& ctx, Binary_Expression* b, Expression* lhs, Expression* rhs)
930
+ {
931
+ Number* l = static_cast<Number*>(lhs);
932
+ Number* r = static_cast<Number*>(rhs);
933
+ double lv = l->value();
934
+ double rv = r->value();
935
+ Binary_Expression::Type op = b->type();
936
+ if (op == Binary_Expression::DIV && !rv) {
937
+ return new (ctx.mem) String_Constant(l->path(), b->position(), "Infinity");
938
+ }
939
+ if (op == Binary_Expression::MOD && !rv) {
940
+ error("division by zero", r->path(), r->position());
941
+ }
942
+
943
+ Number tmp(*r);
944
+ tmp.normalize(l->find_convertible_unit());
945
+ string l_unit(l->unit());
946
+ string r_unit(tmp.unit());
947
+ if (l_unit != r_unit && !l_unit.empty() && !r_unit.empty() &&
948
+ (op == Binary_Expression::ADD || op == Binary_Expression::SUB)) {
949
+ error("cannot add or subtract numbers with incompatible units", l->path(), l->position());
950
+ }
951
+ Number* v = new (ctx.mem) Number(*l);
952
+ v->position(b->position());
953
+ if (l_unit.empty() && (op == Binary_Expression::ADD || op == Binary_Expression::SUB || op == Binary_Expression::MOD)) {
954
+ v->numerator_units() = r->numerator_units();
955
+ v->denominator_units() = r->denominator_units();
956
+ }
957
+
958
+ v->value(ops[op](lv, tmp.value()));
959
+ if (op == Binary_Expression::MUL) {
960
+ for (size_t i = 0, S = r->numerator_units().size(); i < S; ++i) {
961
+ v->numerator_units().push_back(r->numerator_units()[i]);
962
+ }
963
+ for (size_t i = 0, S = r->denominator_units().size(); i < S; ++i) {
964
+ v->denominator_units().push_back(r->denominator_units()[i]);
965
+ }
966
+ }
967
+ else if (op == Binary_Expression::DIV) {
968
+ for (size_t i = 0, S = r->numerator_units().size(); i < S; ++i) {
969
+ v->denominator_units().push_back(r->numerator_units()[i]);
970
+ }
971
+ for (size_t i = 0, S = r->denominator_units().size(); i < S; ++i) {
972
+ v->numerator_units().push_back(r->denominator_units()[i]);
973
+ }
974
+ }
975
+ v->normalize();
976
+ return v;
977
+ }
978
+
979
+ Expression* op_number_color(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression* rhs)
980
+ {
981
+ Number* l = static_cast<Number*>(lhs);
982
+ Color* r = static_cast<Color*>(rhs);
983
+ // TODO: currently SASS converts colors to standard form when adding to strings;
984
+ // when https://github.com/nex3/sass/issues/363 is added this can be removed to
985
+ // preserve the original value
986
+ r->disp("");
987
+ double lv = l->value();
988
+ switch (op) {
989
+ case Binary_Expression::ADD:
990
+ case Binary_Expression::MUL: {
991
+ return new (ctx.mem) Color(l->path(),
992
+ l->position(),
993
+ ops[op](lv, r->r()),
994
+ ops[op](lv, r->g()),
995
+ ops[op](lv, r->b()),
996
+ r->a());
997
+ } break;
998
+ case Binary_Expression::SUB:
999
+ case Binary_Expression::DIV: {
1000
+ string sep(op == Binary_Expression::SUB ? "-" : "/");
1001
+ To_String to_string;
1002
+ string color(r->sixtuplet() ? r->perform(&to_string) :
1003
+ Util::normalize_sixtuplet(r->perform(&to_string)));
1004
+ return new (ctx.mem) String_Constant(l->path(),
1005
+ l->position(),
1006
+ l->perform(&to_string)
1007
+ + sep
1008
+ + color);
1009
+ } break;
1010
+ case Binary_Expression::MOD: {
1011
+ error("cannot divide a number by a color", r->path(), r->position());
1012
+ } break;
1013
+ default: break; // caller should ensure that we don't get here
1014
+ }
1015
+ // unreachable
1016
+ return l;
1017
+ }
1018
+
1019
+ Expression* op_color_number(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression* rhs)
1020
+ {
1021
+ Color* l = static_cast<Color*>(lhs);
1022
+ Number* r = static_cast<Number*>(rhs);
1023
+ double rv = r->value();
1024
+ if (op == Binary_Expression::DIV && !rv) error("division by zero", r->path(), r->position());
1025
+ return new (ctx.mem) Color(l->path(),
1026
+ l->position(),
1027
+ ops[op](l->r(), rv),
1028
+ ops[op](l->g(), rv),
1029
+ ops[op](l->b(), rv),
1030
+ l->a());
1031
+ }
1032
+
1033
+ Expression* op_colors(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression* rhs)
1034
+ {
1035
+ Color* l = static_cast<Color*>(lhs);
1036
+ Color* r = static_cast<Color*>(rhs);
1037
+ if (l->a() != r->a()) {
1038
+ error("alpha channels must be equal when combining colors", r->path(), r->position());
1039
+ }
1040
+ if ((op == Binary_Expression::DIV || op == Binary_Expression::MOD) &&
1041
+ (!r->r() || !r->g() ||!r->b())) {
1042
+ error("division by zero", r->path(), r->position());
1043
+ }
1044
+ return new (ctx.mem) Color(l->path(),
1045
+ l->position(),
1046
+ ops[op](l->r(), r->r()),
1047
+ ops[op](l->g(), r->g()),
1048
+ ops[op](l->b(), r->b()),
1049
+ l->a());
1050
+ }
1051
+
1052
+ Expression* op_strings(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression*rhs)
1053
+ {
1054
+ To_String to_string;
1055
+ Expression::Concrete_Type ltype = lhs->concrete_type();
1056
+ Expression::Concrete_Type rtype = rhs->concrete_type();
1057
+
1058
+ string lstr(lhs->perform(&to_string));
1059
+ string rstr(rhs->perform(&to_string));
1060
+
1061
+ bool l_str_quoted = ((Sass::String*)lhs) && ((Sass::String*)lhs)->needs_unquoting();
1062
+ bool r_str_quoted = ((Sass::String*)rhs) && ((Sass::String*)rhs)->needs_unquoting();
1063
+ bool l_str_color = ltype == Expression::STRING && ctx.names_to_colors.count(lstr) && !l_str_quoted;
1064
+ bool r_str_color = rtype == Expression::STRING && ctx.names_to_colors.count(rstr) && !r_str_quoted;
1065
+
1066
+ bool unquoted = false;
1067
+ if (ltype == Expression::STRING && lstr[0] != '"' && lstr[0] != '\'') unquoted = true;
1068
+ if (l_str_color && r_str_color) {
1069
+ return op_colors(ctx, op, ctx.names_to_colors[lstr], ctx.names_to_colors[rstr]);
1070
+ }
1071
+ else if (l_str_color && rtype == Expression::COLOR) {
1072
+ return op_colors(ctx, op, ctx.names_to_colors[lstr], rhs);
1073
+ }
1074
+ else if (l_str_color && rtype == Expression::NUMBER) {
1075
+ return op_color_number(ctx, op, ctx.names_to_colors[lstr], rhs);
1076
+ }
1077
+ else if (ltype == Expression::COLOR && r_str_color) {
1078
+ return op_number_color(ctx, op, lhs, ctx.names_to_colors[rstr]);
1079
+ }
1080
+ else if (ltype == Expression::NUMBER && r_str_color) {
1081
+ return op_number_color(ctx, op, lhs, ctx.names_to_colors[rstr]);
1082
+ }
1083
+ if (op == Binary_Expression::MUL) error("invalid operands for multiplication", lhs->path(), lhs->position());
1084
+ if (op == Binary_Expression::MOD) error("invalid operands for modulo", lhs->path(), lhs->position());
1085
+ string sep;
1086
+ switch (op) {
1087
+ case Binary_Expression::SUB: sep = "-"; break;
1088
+ case Binary_Expression::DIV: sep = "/"; break;
1089
+ default: break;
1090
+ }
1091
+ if (ltype == Expression::NULL_VAL) error("invalid null operation: \"null plus "+quote(unquote(rstr), '"')+"\".", lhs->path(), lhs->position());
1092
+ if (rtype == Expression::NULL_VAL) error("invalid null operation: \""+quote(unquote(lstr), '"')+" plus null\".", lhs->path(), lhs->position());
1093
+ char q = '\0';
1094
+ if (lstr[0] == '"' || lstr[0] == '\'') q = lstr[0];
1095
+ else if (rstr[0] == '"' || rstr[0] == '\'') q = rstr[0];
1096
+ string result(unquote(lstr) + sep + unquote(rstr));
1097
+ return new (ctx.mem) String_Constant(lhs->path(),
1098
+ lhs->position(),
1099
+ unquoted ? result : quote(result, q));
1100
+ }
1101
+
1102
+ Expression* cval_to_astnode(Sass_Value* v, Context& ctx, Backtrace* backtrace, string path, Position position)
1103
+ {
1104
+ using std::strlen;
1105
+ using std::strcpy;
1106
+ Expression* e = 0;
1107
+ switch (sass_value_get_tag(v)) {
1108
+ case SASS_BOOLEAN: {
1109
+ e = new (ctx.mem) Boolean(path, position, !!sass_boolean_get_value(v));
1110
+ } break;
1111
+ case SASS_NUMBER: {
1112
+ e = new (ctx.mem) Number(path, position, sass_number_get_value(v), sass_number_get_unit(v));
1113
+ } break;
1114
+ case SASS_COLOR: {
1115
+ e = new (ctx.mem) Color(path, position, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v));
1116
+ } break;
1117
+ case SASS_STRING: {
1118
+ e = new (ctx.mem) String_Constant(path, position, sass_string_get_value(v));
1119
+ } break;
1120
+ case SASS_LIST: {
1121
+ List* l = new (ctx.mem) List(path, position, sass_list_get_length(v), sass_list_get_separator(v) == SASS_COMMA ? List::COMMA : List::SPACE);
1122
+ for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) {
1123
+ *l << cval_to_astnode(sass_list_get_value(v, i), ctx, backtrace, path, position);
1124
+ }
1125
+ e = l;
1126
+ } break;
1127
+ case SASS_MAP: {
1128
+ Map* m = new (ctx.mem) Map(path, position);
1129
+ for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {
1130
+ *m << std::make_pair(
1131
+ cval_to_astnode(sass_map_get_key(v, i), ctx, backtrace, path, position),
1132
+ cval_to_astnode(sass_map_get_value(v, i), ctx, backtrace, path, position));
1133
+ }
1134
+ e = m;
1135
+ } break;
1136
+ case SASS_NULL: {
1137
+ e = new (ctx.mem) Null(path, position);
1138
+ } break;
1139
+ case SASS_ERROR: {
1140
+ error("Error in C function: " + string(sass_error_get_message(v)), path, position, backtrace);
1141
+ } break;
1142
+ case SASS_WARNING: {
1143
+ error("Warning in C function: " + string(sass_warning_get_message(v)), path, position, backtrace);
1144
+ } break;
1145
+ }
1146
+ return e;
1147
+ }
1148
+
1149
+ }