sassc 0.0.1

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