1 | // sass.hpp must go before all system headers to get the |
2 | // __EXTENSIONS__ fix on Solaris. |
3 | #include "sass.hpp" |
4 | |
5 | #include <cstdlib> |
6 | #include <cmath> |
7 | #include <iostream> |
8 | #include <sstream> |
9 | #include <iomanip> |
10 | #include <typeinfo> |
11 | |
12 | #include "file.hpp" |
13 | #include "eval.hpp" |
14 | #include "ast.hpp" |
15 | #include "bind.hpp" |
16 | #include "util.hpp" |
17 | #include "inspect.hpp" |
18 | #include "operators.hpp" |
19 | #include "environment.hpp" |
20 | #include "position.hpp" |
21 | #include "sass/values.h" |
22 | #include "to_value.hpp" |
23 | #include "ast2c.hpp" |
24 | #include "c2ast.hpp" |
25 | #include "context.hpp" |
26 | #include "backtrace.hpp" |
27 | #include "lexer.hpp" |
28 | #include "prelexer.hpp" |
29 | #include "parser.hpp" |
30 | #include "expand.hpp" |
31 | #include "color_maps.hpp" |
32 | #include "sass_functions.hpp" |
33 | #include "error_handling.hpp" |
34 | #include "util_string.hpp" |
35 | |
36 | namespace Sass { |
37 | |
38 | Eval::Eval(Expand& exp) |
39 | : exp(exp), |
40 | ctx(exp.ctx), |
41 | traces(exp.traces), |
42 | force(false), |
43 | is_in_comment(false), |
44 | is_in_selector_schema(false) |
45 | { |
46 | bool_true = SASS_MEMORY_NEW(Boolean, "[NA]" , true); |
47 | bool_false = SASS_MEMORY_NEW(Boolean, "[NA]" , false); |
48 | } |
49 | Eval::~Eval() { } |
50 | |
51 | Env* Eval::environment() |
52 | { |
53 | return exp.environment(); |
54 | } |
55 | |
56 | const sass::string Eval::cwd() |
57 | { |
58 | return ctx.cwd(); |
59 | } |
60 | |
61 | struct Sass_Inspect_Options& Eval::options() |
62 | { |
63 | return ctx.c_options; |
64 | } |
65 | |
66 | struct Sass_Compiler* Eval::compiler() |
67 | { |
68 | return ctx.c_compiler; |
69 | } |
70 | |
71 | EnvStack& Eval::env_stack() |
72 | { |
73 | return exp.env_stack; |
74 | } |
75 | |
76 | sass::vector<Sass_Callee>& Eval::callee_stack() |
77 | { |
78 | return ctx.callee_stack; |
79 | } |
80 | |
81 | Expression* Eval::operator()(Block* b) |
82 | { |
83 | Expression* val = 0; |
84 | for (size_t i = 0, L = b->length(); i < L; ++i) { |
85 | val = b->at(i)->perform(op: this); |
86 | if (val) return val; |
87 | } |
88 | return val; |
89 | } |
90 | |
91 | Expression* Eval::operator()(Assignment* a) |
92 | { |
93 | Env* env = environment(); |
94 | sass::string var(a->variable()); |
95 | if (a->is_global()) { |
96 | if (!env->has_global(key: var)) { |
97 | deprecated( |
98 | msg: "!global assignments won't be able to declare new variables in future versions." , |
99 | msg2: "Consider adding `" + var + ": null` at the top level." , |
100 | with_column: true, pstate: a->pstate()); |
101 | } |
102 | if (a->is_default()) { |
103 | if (env->has_global(key: var)) { |
104 | Expression* e = Cast<Expression>(ptr: env->get_global(key: var)); |
105 | if (!e || e->concrete_type() == Expression::NULL_VAL) { |
106 | env->set_global(key: var, val: a->value()->perform(op: this)); |
107 | } |
108 | } |
109 | else { |
110 | env->set_global(key: var, val: a->value()->perform(op: this)); |
111 | } |
112 | } |
113 | else { |
114 | env->set_global(key: var, val: a->value()->perform(op: this)); |
115 | } |
116 | } |
117 | else if (a->is_default()) { |
118 | if (env->has_lexical(key: var)) { |
119 | auto cur = env; |
120 | while (cur && cur->is_lexical()) { |
121 | if (cur->has_local(key: var)) { |
122 | if (AST_Node_Obj node = cur->get_local(key: var)) { |
123 | Expression* e = Cast<Expression>(ptr: node); |
124 | if (!e || e->concrete_type() == Expression::NULL_VAL) { |
125 | cur->set_local(key: var, val: a->value()->perform(op: this)); |
126 | } |
127 | } |
128 | else { |
129 | throw std::runtime_error("Env not in sync" ); |
130 | } |
131 | return 0; |
132 | } |
133 | cur = cur->parent(); |
134 | } |
135 | throw std::runtime_error("Env not in sync" ); |
136 | } |
137 | else if (env->has_global(key: var)) { |
138 | if (AST_Node_Obj node = env->get_global(key: var)) { |
139 | Expression* e = Cast<Expression>(ptr: node); |
140 | if (!e || e->concrete_type() == Expression::NULL_VAL) { |
141 | env->set_global(key: var, val: a->value()->perform(op: this)); |
142 | } |
143 | } |
144 | } |
145 | else if (env->is_lexical()) { |
146 | env->set_local(key: var, val: a->value()->perform(op: this)); |
147 | } |
148 | else { |
149 | env->set_local(key: var, val: a->value()->perform(op: this)); |
150 | } |
151 | } |
152 | else { |
153 | env->set_lexical(key: var, val: a->value()->perform(op: this)); |
154 | } |
155 | return 0; |
156 | } |
157 | |
158 | Expression* Eval::operator()(If* i) |
159 | { |
160 | ExpressionObj rv; |
161 | Env env(environment()); |
162 | env_stack().push_back(x: &env); |
163 | ExpressionObj cond = i->predicate()->perform(op: this); |
164 | if (!cond->is_false()) { |
165 | rv = i->block()->perform(op: this); |
166 | } |
167 | else { |
168 | Block_Obj alt = i->alternative(); |
169 | if (alt) rv = alt->perform(op: this); |
170 | } |
171 | env_stack().pop_back(); |
172 | return rv.detach(); |
173 | } |
174 | |
175 | // For does not create a new env scope |
176 | // But iteration vars are reset afterwards |
177 | Expression* Eval::operator()(ForRule* f) |
178 | { |
179 | sass::string variable(f->variable()); |
180 | ExpressionObj low = f->lower_bound()->perform(op: this); |
181 | if (low->concrete_type() != Expression::NUMBER) { |
182 | traces.push_back(x: Backtrace(low->pstate())); |
183 | throw Exception::TypeMismatch(traces, *low, "integer" ); |
184 | } |
185 | ExpressionObj high = f->upper_bound()->perform(op: this); |
186 | if (high->concrete_type() != Expression::NUMBER) { |
187 | traces.push_back(x: Backtrace(high->pstate())); |
188 | throw Exception::TypeMismatch(traces, *high, "integer" ); |
189 | } |
190 | Number_Obj sass_start = Cast<Number>(ptr: low); |
191 | Number_Obj sass_end = Cast<Number>(ptr: high); |
192 | // check if units are valid for sequence |
193 | if (sass_start->unit() != sass_end->unit()) { |
194 | sass::ostream msg; msg << "Incompatible units: '" |
195 | << sass_end->unit() << "' and '" |
196 | << sass_start->unit() << "'." ; |
197 | error(msg: msg.str(), pstate: low->pstate(), traces); |
198 | } |
199 | double start = sass_start->value(); |
200 | double end = sass_end->value(); |
201 | // only create iterator once in this environment |
202 | Env env(environment(), true); |
203 | env_stack().push_back(x: &env); |
204 | Block_Obj body = f->block(); |
205 | Expression* val = 0; |
206 | if (start < end) { |
207 | if (f->is_inclusive()) ++end; |
208 | for (double i = start; |
209 | i < end; |
210 | ++i) { |
211 | Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit()); |
212 | env.set_local(key: variable, val: it); |
213 | val = body->perform(op: this); |
214 | if (val) break; |
215 | } |
216 | } else { |
217 | if (f->is_inclusive()) --end; |
218 | for (double i = start; |
219 | i > end; |
220 | --i) { |
221 | Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit()); |
222 | env.set_local(key: variable, val: it); |
223 | val = body->perform(op: this); |
224 | if (val) break; |
225 | } |
226 | } |
227 | env_stack().pop_back(); |
228 | return val; |
229 | } |
230 | |
231 | // Eval does not create a new env scope |
232 | // But iteration vars are reset afterwards |
233 | Expression* Eval::operator()(EachRule* e) |
234 | { |
235 | sass::vector<sass::string> variables(e->variables()); |
236 | ExpressionObj expr = e->list()->perform(op: this); |
237 | Env env(environment(), true); |
238 | env_stack().push_back(x: &env); |
239 | List_Obj list; |
240 | Map* map = nullptr; |
241 | if (expr->concrete_type() == Expression::MAP) { |
242 | map = Cast<Map>(ptr: expr); |
243 | } |
244 | else if (SelectorList * ls = Cast<SelectorList>(ptr: expr)) { |
245 | ExpressionObj rv = Listize::perform(node: ls); |
246 | list = Cast<List>(ptr: rv); |
247 | } |
248 | else if (expr->concrete_type() != Expression::LIST) { |
249 | list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA); |
250 | list->append(element: expr); |
251 | } |
252 | else { |
253 | list = Cast<List>(ptr: expr); |
254 | } |
255 | |
256 | Block_Obj body = e->block(); |
257 | ExpressionObj val; |
258 | |
259 | if (map) { |
260 | for (ExpressionObj key : map->keys()) { |
261 | ExpressionObj value = map->at(k: key); |
262 | |
263 | if (variables.size() == 1) { |
264 | List* variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE); |
265 | variable->append(element: key); |
266 | variable->append(element: value); |
267 | env.set_local(key: variables[0], val: variable); |
268 | } else { |
269 | env.set_local(key: variables[0], val: key); |
270 | env.set_local(key: variables[1], val: value); |
271 | } |
272 | |
273 | val = body->perform(op: this); |
274 | if (val) break; |
275 | } |
276 | } |
277 | else { |
278 | if (list->length() == 1 && Cast<SelectorList>(ptr: list)) { |
279 | list = Cast<List>(ptr: list); |
280 | } |
281 | for (size_t i = 0, L = list->length(); i < L; ++i) { |
282 | Expression* item = list->at(i); |
283 | // unwrap value if the expression is an argument |
284 | if (Argument* arg = Cast<Argument>(ptr: item)) item = arg->value(); |
285 | // check if we got passed a list of args (investigate) |
286 | if (List* scalars = Cast<List>(ptr: item)) { |
287 | if (variables.size() == 1) { |
288 | Expression* var = scalars; |
289 | env.set_local(key: variables[0], val: var); |
290 | } else { |
291 | // https://github.com/sass/libsass/issues/3078 |
292 | for (size_t j = 0, K = variables.size(); j < K; ++j) { |
293 | env.set_local(key: variables[j], val: j >= scalars->length() |
294 | ? SASS_MEMORY_NEW(Null, expr->pstate()) : scalars->at(i: j)); |
295 | } |
296 | } |
297 | } else { |
298 | if (variables.size() > 0) { |
299 | env.set_local(key: variables.at(n: 0), val: item); |
300 | for (size_t j = 1, K = variables.size(); j < K; ++j) { |
301 | // XXX: this is never hit via spec tests |
302 | Expression* res = SASS_MEMORY_NEW(Null, expr->pstate()); |
303 | env.set_local(key: variables[j], val: res); |
304 | } |
305 | } |
306 | } |
307 | val = body->perform(op: this); |
308 | if (val) break; |
309 | } |
310 | } |
311 | env_stack().pop_back(); |
312 | return val.detach(); |
313 | } |
314 | |
315 | Expression* Eval::operator()(WhileRule* w) |
316 | { |
317 | ExpressionObj pred = w->predicate(); |
318 | Block_Obj body = w->block(); |
319 | Env env(environment(), true); |
320 | env_stack().push_back(x: &env); |
321 | ExpressionObj cond = pred->perform(op: this); |
322 | while (!cond->is_false()) { |
323 | ExpressionObj val = body->perform(op: this); |
324 | if (val) { |
325 | env_stack().pop_back(); |
326 | return val.detach(); |
327 | } |
328 | cond = pred->perform(op: this); |
329 | } |
330 | env_stack().pop_back(); |
331 | return 0; |
332 | } |
333 | |
334 | Expression* Eval::operator()(Return* r) |
335 | { |
336 | return r->value()->perform(op: this); |
337 | } |
338 | |
339 | Expression* Eval::operator()(WarningRule* w) |
340 | { |
341 | Sass_Output_Style outstyle = options().output_style; |
342 | options().output_style = NESTED; |
343 | ExpressionObj message = w->message()->perform(op: this); |
344 | Env* env = environment(); |
345 | |
346 | // try to use generic function |
347 | if (env->has(key: "@warn[f]" )) { |
348 | |
349 | // add call stack entry |
350 | callee_stack().push_back(x: { |
351 | .name: "@warn" , |
352 | .path: w->pstate().getPath(), |
353 | .line: w->pstate().getLine(), |
354 | .column: w->pstate().getColumn(), |
355 | .type: SASS_CALLEE_FUNCTION, |
356 | .env: { .frame: env } |
357 | }); |
358 | |
359 | Definition* def = Cast<Definition>(ptr: (*env)["@warn[f]" ]); |
360 | // Block_Obj body = def->block(); |
361 | // Native_Function func = def->native_function(); |
362 | Sass_Function_Entry c_function = def->c_function(); |
363 | Sass_Function_Fn c_func = sass_function_get_function(cb: c_function); |
364 | |
365 | AST2C ast2c; |
366 | union Sass_Value* c_args = sass_make_list(len: 1, sep: SASS_COMMA, is_bracketed: false); |
367 | sass_list_set_value(v: c_args, i: 0, value: message->perform(op: &ast2c)); |
368 | union Sass_Value* c_val = c_func(c_args, c_function, compiler()); |
369 | options().output_style = outstyle; |
370 | callee_stack().pop_back(); |
371 | sass_delete_value(val: c_args); |
372 | sass_delete_value(val: c_val); |
373 | return 0; |
374 | |
375 | } |
376 | |
377 | sass::string result(unquote(message->to_sass())); |
378 | std::cerr << "WARNING: " << result << std::endl; |
379 | traces.push_back(x: Backtrace(w->pstate())); |
380 | std::cerr << traces_to_string(traces, indent: " " ); |
381 | std::cerr << std::endl; |
382 | options().output_style = outstyle; |
383 | traces.pop_back(); |
384 | return 0; |
385 | } |
386 | |
387 | Expression* Eval::operator()(ErrorRule* e) |
388 | { |
389 | Sass_Output_Style outstyle = options().output_style; |
390 | options().output_style = NESTED; |
391 | ExpressionObj message = e->message()->perform(op: this); |
392 | Env* env = environment(); |
393 | |
394 | // try to use generic function |
395 | if (env->has(key: "@error[f]" )) { |
396 | |
397 | // add call stack entry |
398 | callee_stack().push_back(x: { |
399 | .name: "@error" , |
400 | .path: e->pstate().getPath(), |
401 | .line: e->pstate().getLine(), |
402 | .column: e->pstate().getColumn(), |
403 | .type: SASS_CALLEE_FUNCTION, |
404 | .env: { .frame: env } |
405 | }); |
406 | |
407 | Definition* def = Cast<Definition>(ptr: (*env)["@error[f]" ]); |
408 | // Block_Obj body = def->block(); |
409 | // Native_Function func = def->native_function(); |
410 | Sass_Function_Entry c_function = def->c_function(); |
411 | Sass_Function_Fn c_func = sass_function_get_function(cb: c_function); |
412 | |
413 | AST2C ast2c; |
414 | union Sass_Value* c_args = sass_make_list(len: 1, sep: SASS_COMMA, is_bracketed: false); |
415 | sass_list_set_value(v: c_args, i: 0, value: message->perform(op: &ast2c)); |
416 | union Sass_Value* c_val = c_func(c_args, c_function, compiler()); |
417 | options().output_style = outstyle; |
418 | callee_stack().pop_back(); |
419 | sass_delete_value(val: c_args); |
420 | sass_delete_value(val: c_val); |
421 | return 0; |
422 | |
423 | } |
424 | |
425 | sass::string result(unquote(message->to_sass())); |
426 | options().output_style = outstyle; |
427 | error(msg: result, pstate: e->pstate(), traces); |
428 | return 0; |
429 | } |
430 | |
431 | Expression* Eval::operator()(DebugRule* d) |
432 | { |
433 | Sass_Output_Style outstyle = options().output_style; |
434 | options().output_style = NESTED; |
435 | ExpressionObj message = d->value()->perform(op: this); |
436 | Env* env = environment(); |
437 | |
438 | // try to use generic function |
439 | if (env->has(key: "@debug[f]" )) { |
440 | |
441 | // add call stack entry |
442 | callee_stack().push_back(x: { |
443 | .name: "@debug" , |
444 | .path: d->pstate().getPath(), |
445 | .line: d->pstate().getLine(), |
446 | .column: d->pstate().getColumn(), |
447 | .type: SASS_CALLEE_FUNCTION, |
448 | .env: { .frame: env } |
449 | }); |
450 | |
451 | Definition* def = Cast<Definition>(ptr: (*env)["@debug[f]" ]); |
452 | // Block_Obj body = def->block(); |
453 | // Native_Function func = def->native_function(); |
454 | Sass_Function_Entry c_function = def->c_function(); |
455 | Sass_Function_Fn c_func = sass_function_get_function(cb: c_function); |
456 | |
457 | AST2C ast2c; |
458 | union Sass_Value* c_args = sass_make_list(len: 1, sep: SASS_COMMA, is_bracketed: false); |
459 | sass_list_set_value(v: c_args, i: 0, value: message->perform(op: &ast2c)); |
460 | union Sass_Value* c_val = c_func(c_args, c_function, compiler()); |
461 | options().output_style = outstyle; |
462 | callee_stack().pop_back(); |
463 | sass_delete_value(val: c_args); |
464 | sass_delete_value(val: c_val); |
465 | return 0; |
466 | |
467 | } |
468 | |
469 | sass::string result(unquote(message->to_sass())); |
470 | sass::string abs_path(Sass::File::rel2abs(path: d->pstate().getPath(), base: cwd(), cwd: cwd())); |
471 | sass::string rel_path(Sass::File::abs2rel(path: d->pstate().getPath(), base: cwd(), cwd: cwd())); |
472 | sass::string output_path(Sass::File::path_for_console(rel_path, abs_path, orig_path: d->pstate().getPath())); |
473 | options().output_style = outstyle; |
474 | |
475 | std::cerr << output_path << ":" << d->pstate().getLine() << " DEBUG: " << result; |
476 | std::cerr << std::endl; |
477 | return 0; |
478 | } |
479 | |
480 | |
481 | Expression* Eval::operator()(List* l) |
482 | { |
483 | // special case for unevaluated map |
484 | if (l->separator() == SASS_HASH) { |
485 | Map_Obj lm = SASS_MEMORY_NEW(Map, |
486 | l->pstate(), |
487 | l->length() / 2); |
488 | for (size_t i = 0, L = l->length(); i < L; i += 2) |
489 | { |
490 | ExpressionObj key = (*l)[i+0]->perform(op: this); |
491 | ExpressionObj val = (*l)[i+1]->perform(op: this); |
492 | // make sure the color key never displays its real name |
493 | key->is_delayed(is_delayed__: true); // verified |
494 | *lm << std::make_pair(x&: key, y&: val); |
495 | } |
496 | if (lm->has_duplicate_key()) { |
497 | traces.push_back(x: Backtrace(l->pstate())); |
498 | throw Exception::DuplicateKeyError(traces, *lm, *l); |
499 | } |
500 | |
501 | lm->is_interpolant(is_interpolant__: l->is_interpolant()); |
502 | return lm->perform(op: this); |
503 | } |
504 | // check if we should expand it |
505 | if (l->is_expanded()) return l; |
506 | // regular case for unevaluated lists |
507 | List_Obj ll = SASS_MEMORY_NEW(List, |
508 | l->pstate(), |
509 | l->length(), |
510 | l->separator(), |
511 | l->is_arglist(), |
512 | l->is_bracketed()); |
513 | for (size_t i = 0, L = l->length(); i < L; ++i) { |
514 | ll->append(element: (*l)[i]->perform(op: this)); |
515 | } |
516 | ll->is_interpolant(is_interpolant__: l->is_interpolant()); |
517 | ll->from_selector(from_selector__: l->from_selector()); |
518 | ll->is_expanded(is_expanded__: true); |
519 | return ll.detach(); |
520 | } |
521 | |
522 | Expression* Eval::operator()(Map* m) |
523 | { |
524 | if (m->is_expanded()) return m; |
525 | |
526 | // make sure we're not starting with duplicate keys. |
527 | // the duplicate key state will have been set in the parser phase. |
528 | if (m->has_duplicate_key()) { |
529 | traces.push_back(x: Backtrace(m->pstate())); |
530 | throw Exception::DuplicateKeyError(traces, *m, *m); |
531 | } |
532 | |
533 | Map_Obj mm = SASS_MEMORY_NEW(Map, |
534 | m->pstate(), |
535 | m->length()); |
536 | for (auto key : m->keys()) { |
537 | Expression* ex_key = key->perform(op: this); |
538 | Expression* ex_val = m->at(k: key); |
539 | if (ex_val == NULL) continue; |
540 | ex_val = ex_val->perform(op: this); |
541 | *mm << std::make_pair(x&: ex_key, y&: ex_val); |
542 | } |
543 | |
544 | // check the evaluated keys aren't duplicates. |
545 | if (mm->has_duplicate_key()) { |
546 | traces.push_back(x: Backtrace(m->pstate())); |
547 | throw Exception::DuplicateKeyError(traces, *mm, *m); |
548 | } |
549 | |
550 | mm->is_expanded(is_expanded__: true); |
551 | return mm.detach(); |
552 | } |
553 | |
554 | Expression* Eval::operator()(Binary_Expression* b_in) |
555 | { |
556 | |
557 | ExpressionObj lhs = b_in->left(); |
558 | ExpressionObj rhs = b_in->right(); |
559 | enum Sass_OP op_type = b_in->optype(); |
560 | |
561 | if (op_type == Sass_OP::AND) { |
562 | // LOCAL_FLAG(force, true); |
563 | lhs = lhs->perform(op: this); |
564 | if (!*lhs) return lhs.detach(); |
565 | return rhs->perform(op: this); |
566 | } |
567 | else if (op_type == Sass_OP::OR) { |
568 | // LOCAL_FLAG(force, true); |
569 | lhs = lhs->perform(op: this); |
570 | if (*lhs) return lhs.detach(); |
571 | return rhs->perform(op: this); |
572 | } |
573 | |
574 | // Evaluate variables as early o |
575 | while (Variable* l_v = Cast<Variable>(ptr: lhs)) { |
576 | lhs = operator()(l_v); |
577 | } |
578 | while (Variable* r_v = Cast<Variable>(ptr: rhs)) { |
579 | rhs = operator()(r_v); |
580 | } |
581 | |
582 | Binary_ExpressionObj b = b_in; |
583 | |
584 | // Evaluate sub-expressions early on |
585 | while (Binary_Expression* l_b = Cast<Binary_Expression>(ptr: lhs)) { |
586 | if (!force && l_b->is_delayed()) break; |
587 | lhs = operator()(b_in: l_b); |
588 | } |
589 | while (Binary_Expression* r_b = Cast<Binary_Expression>(ptr: rhs)) { |
590 | if (!force && r_b->is_delayed()) break; |
591 | rhs = operator()(b_in: r_b); |
592 | } |
593 | |
594 | // don't eval delayed expressions (the '/' when used as a separator) |
595 | if (!force && op_type == Sass_OP::DIV && b->is_delayed()) { |
596 | b->right(right__: b->right()->perform(op: this)); |
597 | b->left(left__: b->left()->perform(op: this)); |
598 | return b.detach(); |
599 | } |
600 | |
601 | // specific types we know are final |
602 | // handle them early to avoid overhead |
603 | if (Number* l_n = Cast<Number>(ptr: lhs)) { |
604 | // lhs is number and rhs is number |
605 | if (Number* r_n = Cast<Number>(ptr: rhs)) { |
606 | try { |
607 | switch (op_type) { |
608 | case Sass_OP::EQ: return *l_n == *r_n ? bool_true : bool_false; |
609 | case Sass_OP::NEQ: return *l_n == *r_n ? bool_false : bool_true; |
610 | case Sass_OP::LT: return *l_n < *r_n ? bool_true : bool_false; |
611 | case Sass_OP::GTE: return *l_n < *r_n ? bool_false : bool_true; |
612 | case Sass_OP::LTE: return *l_n < *r_n || *l_n == *r_n ? bool_true : bool_false; |
613 | case Sass_OP::GT: return *l_n < *r_n || *l_n == *r_n ? bool_false : bool_true; |
614 | case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: |
615 | return Operators::op_numbers(op_type, *l_n, *r_n, opt: options(), pstate: b_in->pstate()); |
616 | default: break; |
617 | } |
618 | } |
619 | catch (Exception::OperationError& err) |
620 | { |
621 | traces.push_back(x: Backtrace(b_in->pstate())); |
622 | throw Exception::SassValueError(traces, b_in->pstate(), err); |
623 | } |
624 | } |
625 | // lhs is number and rhs is color |
626 | // Todo: allow to work with HSLA colors |
627 | else if (Color* r_col = Cast<Color>(ptr: rhs)) { |
628 | Color_RGBA_Obj r_c = r_col->toRGBA(); |
629 | try { |
630 | switch (op_type) { |
631 | case Sass_OP::EQ: return *l_n == *r_c ? bool_true : bool_false; |
632 | case Sass_OP::NEQ: return *l_n == *r_c ? bool_false : bool_true; |
633 | case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: |
634 | return Operators::op_number_color(op_type, *l_n, *r_c, opt: options(), pstate: b_in->pstate()); |
635 | default: break; |
636 | } |
637 | } |
638 | catch (Exception::OperationError& err) |
639 | { |
640 | traces.push_back(x: Backtrace(b_in->pstate())); |
641 | throw Exception::SassValueError(traces, b_in->pstate(), err); |
642 | } |
643 | } |
644 | } |
645 | else if (Color* l_col = Cast<Color>(ptr: lhs)) { |
646 | Color_RGBA_Obj l_c = l_col->toRGBA(); |
647 | // lhs is color and rhs is color |
648 | if (Color* r_col = Cast<Color>(ptr: rhs)) { |
649 | Color_RGBA_Obj r_c = r_col->toRGBA(); |
650 | try { |
651 | switch (op_type) { |
652 | case Sass_OP::EQ: return *l_c == *r_c ? bool_true : bool_false; |
653 | case Sass_OP::NEQ: return *l_c == *r_c ? bool_false : bool_true; |
654 | case Sass_OP::LT: return *l_c < *r_c ? bool_true : bool_false; |
655 | case Sass_OP::GTE: return *l_c < *r_c ? bool_false : bool_true; |
656 | case Sass_OP::LTE: return *l_c < *r_c || *l_c == *r_c ? bool_true : bool_false; |
657 | case Sass_OP::GT: return *l_c < *r_c || *l_c == *r_c ? bool_false : bool_true; |
658 | case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: |
659 | return Operators::op_colors(op_type, *l_c, *r_c, opt: options(), pstate: b_in->pstate()); |
660 | default: break; |
661 | } |
662 | } |
663 | catch (Exception::OperationError& err) |
664 | { |
665 | traces.push_back(x: Backtrace(b_in->pstate())); |
666 | throw Exception::SassValueError(traces, b_in->pstate(), err); |
667 | } |
668 | } |
669 | // lhs is color and rhs is number |
670 | else if (Number* r_n = Cast<Number>(ptr: rhs)) { |
671 | try { |
672 | switch (op_type) { |
673 | case Sass_OP::EQ: return *l_c == *r_n ? bool_true : bool_false; |
674 | case Sass_OP::NEQ: return *l_c == *r_n ? bool_false : bool_true; |
675 | case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: |
676 | return Operators::op_color_number(op_type, *l_c, *r_n, opt: options(), pstate: b_in->pstate()); |
677 | default: break; |
678 | } |
679 | } |
680 | catch (Exception::OperationError& err) |
681 | { |
682 | traces.push_back(x: Backtrace(b_in->pstate())); |
683 | throw Exception::SassValueError(traces, b_in->pstate(), err); |
684 | } |
685 | } |
686 | } |
687 | |
688 | String_Schema_Obj ret_schema; |
689 | |
690 | // only the last item will be used to eval the binary expression |
691 | if (String_Schema* s_l = Cast<String_Schema>(ptr: b->left())) { |
692 | if (!s_l->has_interpolant() && (!s_l->is_right_interpolant())) { |
693 | ret_schema = SASS_MEMORY_NEW(String_Schema, b->pstate()); |
694 | Binary_ExpressionObj bin_ex = SASS_MEMORY_NEW(Binary_Expression, b->pstate(), |
695 | b->op(), s_l->last(), b->right()); |
696 | bin_ex->is_delayed(is_delayed__: b->left()->is_delayed() || b->right()->is_delayed()); // unverified |
697 | for (size_t i = 0; i < s_l->length() - 1; ++i) { |
698 | ret_schema->append(element: s_l->at(i)->perform(op: this)); |
699 | } |
700 | ret_schema->append(element: bin_ex->perform(op: this)); |
701 | return ret_schema->perform(op: this); |
702 | } |
703 | } |
704 | if (String_Schema* s_r = Cast<String_Schema>(ptr: b->right())) { |
705 | |
706 | if (!s_r->has_interpolant() && (!s_r->is_left_interpolant() || op_type == Sass_OP::DIV)) { |
707 | ret_schema = SASS_MEMORY_NEW(String_Schema, b->pstate()); |
708 | Binary_ExpressionObj bin_ex = SASS_MEMORY_NEW(Binary_Expression, b->pstate(), |
709 | b->op(), b->left(), s_r->first()); |
710 | bin_ex->is_delayed(is_delayed__: b->left()->is_delayed() || b->right()->is_delayed()); // verified |
711 | ret_schema->append(element: bin_ex->perform(op: this)); |
712 | for (size_t i = 1; i < s_r->length(); ++i) { |
713 | ret_schema->append(element: s_r->at(i)->perform(op: this)); |
714 | } |
715 | return ret_schema->perform(op: this); |
716 | } |
717 | } |
718 | |
719 | // fully evaluate their values |
720 | if (op_type == Sass_OP::EQ || |
721 | op_type == Sass_OP::NEQ || |
722 | op_type == Sass_OP::GT || |
723 | op_type == Sass_OP::GTE || |
724 | op_type == Sass_OP::LT || |
725 | op_type == Sass_OP::LTE) |
726 | { |
727 | LOCAL_FLAG(force, true); |
728 | lhs->is_expanded(is_expanded__: false); |
729 | lhs->set_delayed(false); |
730 | lhs = lhs->perform(op: this); |
731 | rhs->is_expanded(is_expanded__: false); |
732 | rhs->set_delayed(false); |
733 | rhs = rhs->perform(op: this); |
734 | } |
735 | else { |
736 | lhs = lhs->perform(op: this); |
737 | } |
738 | |
739 | // not a logical connective, so go ahead and eval the rhs |
740 | rhs = rhs->perform(op: this); |
741 | AST_Node_Obj lu = lhs; |
742 | AST_Node_Obj ru = rhs; |
743 | |
744 | Expression::Type l_type; |
745 | Expression::Type r_type; |
746 | |
747 | // Is one of the operands an interpolant? |
748 | String_Schema_Obj s1 = Cast<String_Schema>(ptr: b->left()); |
749 | String_Schema_Obj s2 = Cast<String_Schema>(ptr: b->right()); |
750 | Binary_ExpressionObj b1 = Cast<Binary_Expression>(ptr: b->left()); |
751 | Binary_ExpressionObj b2 = Cast<Binary_Expression>(ptr: b->right()); |
752 | |
753 | bool schema_op = false; |
754 | |
755 | bool force_delay = (s2 && s2->is_left_interpolant()) || |
756 | (s1 && s1->is_right_interpolant()) || |
757 | (b1 && b1->is_right_interpolant()) || |
758 | (b2 && b2->is_left_interpolant()); |
759 | |
760 | if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants()) || force_delay) |
761 | { |
762 | if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::MOD || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB || |
763 | op_type == Sass_OP::EQ) { |
764 | // If possible upgrade LHS to a number (for number to string compare) |
765 | if (String_Constant* str = Cast<String_Constant>(ptr: lhs)) { |
766 | sass::string value(str->value()); |
767 | const char* start = value.c_str(); |
768 | if (Prelexer::sequence < Prelexer::dimension, Prelexer::end_of_file >(src: start) != 0) { |
769 | lhs = Parser::lexed_dimension(pstate: b->pstate(), parsed: str->value()); |
770 | } |
771 | } |
772 | // If possible upgrade RHS to a number (for string to number compare) |
773 | if (String_Constant* str = Cast<String_Constant>(ptr: rhs)) { |
774 | sass::string value(str->value()); |
775 | const char* start = value.c_str(); |
776 | if (Prelexer::sequence < Prelexer::dimension, Prelexer::number >(src: start) != 0) { |
777 | rhs = Parser::lexed_dimension(pstate: b->pstate(), parsed: str->value()); |
778 | } |
779 | } |
780 | } |
781 | |
782 | To_Value to_value(ctx); |
783 | ValueObj v_l = Cast<Value>(ptr: lhs->perform(op: &to_value)); |
784 | ValueObj v_r = Cast<Value>(ptr: rhs->perform(op: &to_value)); |
785 | |
786 | if (force_delay) { |
787 | sass::string str("" ); |
788 | str += v_l->to_string(opt: options()); |
789 | if (b->op().ws_before) str += " " ; |
790 | str += b->separator(); |
791 | if (b->op().ws_after) str += " " ; |
792 | str += v_r->to_string(opt: options()); |
793 | String_Constant* val = SASS_MEMORY_NEW(String_Constant, b->pstate(), str); |
794 | val->is_interpolant(is_interpolant__: b->left()->has_interpolant()); |
795 | return val; |
796 | } |
797 | } |
798 | |
799 | // see if it's a relational expression |
800 | try { |
801 | switch(op_type) { |
802 | case Sass_OP::EQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::eq(lhs, rhs)); |
803 | case Sass_OP::NEQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::neq(lhs, rhs)); |
804 | case Sass_OP::GT: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::gt(lhs, rhs)); |
805 | case Sass_OP::GTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::gte(lhs, rhs)); |
806 | case Sass_OP::LT: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::lt(lhs, rhs)); |
807 | case Sass_OP::LTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::lte(lhs, rhs)); |
808 | default: break; |
809 | } |
810 | } |
811 | catch (Exception::OperationError& err) |
812 | { |
813 | traces.push_back(x: Backtrace(b->pstate())); |
814 | throw Exception::SassValueError(traces, b->pstate(), err); |
815 | } |
816 | |
817 | l_type = lhs->concrete_type(); |
818 | r_type = rhs->concrete_type(); |
819 | |
820 | // ToDo: throw error in op functions |
821 | // ToDo: then catch and re-throw them |
822 | ExpressionObj rv; |
823 | try { |
824 | SourceSpan pstate(b->pstate()); |
825 | if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) { |
826 | Number* l_n = Cast<Number>(ptr: lhs); |
827 | Number* r_n = Cast<Number>(ptr: rhs); |
828 | l_n->reduce(); r_n->reduce(); |
829 | rv = Operators::op_numbers(op_type, *l_n, *r_n, opt: options(), pstate); |
830 | } |
831 | else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) { |
832 | Number* l_n = Cast<Number>(ptr: lhs); |
833 | Color_RGBA_Obj r_c = Cast<Color>(ptr: rhs)->toRGBA(); |
834 | rv = Operators::op_number_color(op_type, *l_n, *r_c, opt: options(), pstate); |
835 | } |
836 | else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) { |
837 | Color_RGBA_Obj l_c = Cast<Color>(ptr: lhs)->toRGBA(); |
838 | Number* r_n = Cast<Number>(ptr: rhs); |
839 | rv = Operators::op_color_number(op_type, *l_c, *r_n, opt: options(), pstate); |
840 | } |
841 | else if (l_type == Expression::COLOR && r_type == Expression::COLOR) { |
842 | Color_RGBA_Obj l_c = Cast<Color>(ptr: lhs)->toRGBA(); |
843 | Color_RGBA_Obj r_c = Cast<Color>(ptr: rhs)->toRGBA(); |
844 | rv = Operators::op_colors(op_type, *l_c, *r_c, opt: options(), pstate); |
845 | } |
846 | else { |
847 | To_Value to_value(ctx); |
848 | // this will leak if perform does not return a value! |
849 | ValueObj v_l = Cast<Value>(ptr: lhs->perform(op: &to_value)); |
850 | ValueObj v_r = Cast<Value>(ptr: rhs->perform(op: &to_value)); |
851 | bool interpolant = b->is_right_interpolant() || |
852 | b->is_left_interpolant() || |
853 | b->is_interpolant(); |
854 | if (op_type == Sass_OP::SUB) interpolant = false; |
855 | // if (op_type == Sass_OP::DIV) interpolant = true; |
856 | // check for type violations |
857 | if (l_type == Expression::MAP || l_type == Expression::FUNCTION_VAL) { |
858 | traces.push_back(x: Backtrace(v_l->pstate())); |
859 | throw Exception::InvalidValue(traces, *v_l); |
860 | } |
861 | if (r_type == Expression::MAP || l_type == Expression::FUNCTION_VAL) { |
862 | traces.push_back(x: Backtrace(v_r->pstate())); |
863 | throw Exception::InvalidValue(traces, *v_r); |
864 | } |
865 | Value* ex = Operators::op_strings(b->op(), *v_l, *v_r, opt: options(), pstate, delayed: !interpolant); // pass true to compress |
866 | if (String_Constant* str = Cast<String_Constant>(ptr: ex)) |
867 | { |
868 | if (str->concrete_type() == Expression::STRING) |
869 | { |
870 | String_Constant* lstr = Cast<String_Constant>(ptr: lhs); |
871 | String_Constant* rstr = Cast<String_Constant>(ptr: rhs); |
872 | if (op_type != Sass_OP::SUB) { |
873 | if (String_Constant* org = lstr ? lstr : rstr) |
874 | { str->quote_mark(quote_mark__: org->quote_mark()); } |
875 | } |
876 | } |
877 | } |
878 | ex->is_interpolant(is_interpolant__: b->is_interpolant()); |
879 | rv = ex; |
880 | } |
881 | } |
882 | catch (Exception::OperationError& err) |
883 | { |
884 | traces.push_back(x: Backtrace(b->pstate())); |
885 | // throw Exception::Base(b->pstate(), err.what()); |
886 | throw Exception::SassValueError(traces, b->pstate(), err); |
887 | } |
888 | |
889 | if (rv) { |
890 | if (schema_op) { |
891 | // XXX: this is never hit via spec tests |
892 | (*s2)[0] = rv; |
893 | rv = s2->perform(op: this); |
894 | } |
895 | } |
896 | |
897 | return rv.detach(); |
898 | |
899 | } |
900 | |
901 | Expression* Eval::operator()(Unary_Expression* u) |
902 | { |
903 | ExpressionObj operand = u->operand()->perform(op: this); |
904 | if (u->optype() == Unary_Expression::NOT) { |
905 | Boolean* result = SASS_MEMORY_NEW(Boolean, u->pstate(), (bool)*operand); |
906 | result->value(value__: !result->value()); |
907 | return result; |
908 | } |
909 | else if (Number_Obj nr = Cast<Number>(ptr: operand)) { |
910 | // negate value for minus unary expression |
911 | if (u->optype() == Unary_Expression::MINUS) { |
912 | Number_Obj cpy = SASS_MEMORY_COPY(nr); |
913 | cpy->value( value__: - cpy->value() ); // negate value |
914 | return cpy.detach(); // return the copy |
915 | } |
916 | else if (u->optype() == Unary_Expression::SLASH) { |
917 | sass::string str = '/' + nr->to_string(opt: options()); |
918 | return SASS_MEMORY_NEW(String_Constant, u->pstate(), str); |
919 | } |
920 | // nothing for positive |
921 | return nr.detach(); |
922 | } |
923 | else { |
924 | // Special cases: +/- variables which evaluate to null output just +/-, |
925 | // but +/- null itself outputs the string |
926 | if (operand->concrete_type() == Expression::NULL_VAL && Cast<Variable>(ptr: u->operand())) { |
927 | u->operand(SASS_MEMORY_NEW(String_Quoted, u->pstate(), "" )); |
928 | } |
929 | // Never apply unary opertions on colors @see #2140 |
930 | else if (Color* color = Cast<Color>(ptr: operand)) { |
931 | // Use the color name if this was eval with one |
932 | if (color->disp().length() > 0) { |
933 | Unary_ExpressionObj cpy = SASS_MEMORY_COPY(u); |
934 | cpy->operand(SASS_MEMORY_NEW(String_Constant, operand->pstate(), color->disp())); |
935 | return SASS_MEMORY_NEW(String_Quoted, |
936 | cpy->pstate(), |
937 | cpy->inspect()); |
938 | } |
939 | } |
940 | else { |
941 | Unary_ExpressionObj cpy = SASS_MEMORY_COPY(u); |
942 | cpy->operand(operand__: operand); |
943 | return SASS_MEMORY_NEW(String_Quoted, |
944 | cpy->pstate(), |
945 | cpy->inspect()); |
946 | } |
947 | |
948 | return SASS_MEMORY_NEW(String_Quoted, |
949 | u->pstate(), |
950 | u->inspect()); |
951 | } |
952 | // unreachable |
953 | return u; |
954 | } |
955 | |
956 | Expression* Eval::operator()(Function_Call* c) |
957 | { |
958 | if (traces.size() > Constants::MaxCallStack) { |
959 | // XXX: this is never hit via spec tests |
960 | sass::ostream stm; |
961 | stm << "Stack depth exceeded max of " << Constants::MaxCallStack; |
962 | error(msg: stm.str(), pstate: c->pstate(), traces); |
963 | } |
964 | |
965 | if (Cast<String_Schema>(ptr: c->sname())) { |
966 | ExpressionObj evaluated_name = c->sname()->perform(op: this); |
967 | ExpressionObj evaluated_args = c->arguments()->perform(op: this); |
968 | sass::string str(evaluated_name->to_string()); |
969 | str += evaluated_args->to_string(); |
970 | return SASS_MEMORY_NEW(String_Constant, c->pstate(), str); |
971 | } |
972 | |
973 | sass::string name(Util::normalize_underscores(str: c->name())); |
974 | sass::string full_name(name + "[f]" ); |
975 | |
976 | // we make a clone here, need to implement that further |
977 | Arguments_Obj args = c->arguments(); |
978 | |
979 | Env* env = environment(); |
980 | if (!env->has(key: full_name) || (!c->via_call() && Prelexer::re_special_fun(src: name.c_str()))) { |
981 | if (!env->has(key: "*[f]" )) { |
982 | for (Argument_Obj arg : args->elements()) { |
983 | if (List_Obj ls = Cast<List>(ptr: arg->value())) { |
984 | if (ls->size() == 0) error(msg: "() isn't a valid CSS value." , pstate: c->pstate(), traces); |
985 | } |
986 | } |
987 | args = Cast<Arguments>(ptr: args->perform(op: this)); |
988 | Function_Call_Obj lit = SASS_MEMORY_NEW(Function_Call, |
989 | c->pstate(), |
990 | c->name(), |
991 | args); |
992 | if (args->has_named_arguments()) { |
993 | error(msg: "Plain CSS function " + c->name() + " doesn't support keyword arguments" , pstate: c->pstate(), traces); |
994 | } |
995 | String_Quoted* str = SASS_MEMORY_NEW(String_Quoted, |
996 | c->pstate(), |
997 | lit->to_string(options())); |
998 | str->is_interpolant(is_interpolant__: c->is_interpolant()); |
999 | return str; |
1000 | } else { |
1001 | // call generic function |
1002 | full_name = "*[f]" ; |
1003 | } |
1004 | } |
1005 | |
1006 | // further delay for calls |
1007 | if (full_name != "call[f]" ) { |
1008 | args->set_delayed(false); // verified |
1009 | } |
1010 | if (full_name != "if[f]" ) { |
1011 | args = Cast<Arguments>(ptr: args->perform(op: this)); |
1012 | } |
1013 | Definition* def = Cast<Definition>(ptr: (*env)[full_name]); |
1014 | |
1015 | if (c->func()) def = c->func()->definition(); |
1016 | |
1017 | if (def->is_overload_stub()) { |
1018 | sass::ostream ss; |
1019 | size_t L = args->length(); |
1020 | // account for rest arguments |
1021 | if (args->has_rest_argument() && args->length() > 0) { |
1022 | // get the rest arguments list |
1023 | List* rest = Cast<List>(ptr: args->last()->value()); |
1024 | // arguments before rest argument plus rest |
1025 | if (rest) L += rest->length() - 1; |
1026 | } |
1027 | ss << full_name << L; |
1028 | full_name = ss.str(); |
1029 | sass::string resolved_name(full_name); |
1030 | if (!env->has(key: resolved_name)) error(msg: "overloaded function `" + sass::string(c->name()) + "` given wrong number of arguments" , pstate: c->pstate(), traces); |
1031 | def = Cast<Definition>(ptr: (*env)[resolved_name]); |
1032 | } |
1033 | |
1034 | ExpressionObj result = c; |
1035 | Block_Obj body = def->block(); |
1036 | Native_Function func = def->native_function(); |
1037 | Sass_Function_Entry c_function = def->c_function(); |
1038 | |
1039 | if (c->is_css()) return result.detach(); |
1040 | |
1041 | Parameters_Obj params = def->parameters(); |
1042 | Env fn_env(def->environment()); |
1043 | env_stack().push_back(x: &fn_env); |
1044 | |
1045 | if (func || body) { |
1046 | bind(type: sass::string("Function" ), name: c->name(), params, args, &fn_env, this, traces); |
1047 | sass::string msg(", in function `" + c->name() + "`" ); |
1048 | traces.push_back(x: Backtrace(c->pstate(), msg)); |
1049 | callee_stack().push_back(x: { |
1050 | .name: c->name().c_str(), |
1051 | .path: c->pstate().getPath(), |
1052 | .line: c->pstate().getLine(), |
1053 | .column: c->pstate().getColumn(), |
1054 | .type: SASS_CALLEE_FUNCTION, |
1055 | .env: { .frame: env } |
1056 | }); |
1057 | |
1058 | // eval the body if user-defined or special, invoke underlying CPP function if native |
1059 | if (body /* && !Prelexer::re_special_fun(name.c_str()) */) { |
1060 | result = body->perform(op: this); |
1061 | } |
1062 | else if (func) { |
1063 | result = func(fn_env, *env, ctx, def->signature(), c->pstate(), traces, exp.getSelectorStack(), exp.originalStack); |
1064 | } |
1065 | if (!result) { |
1066 | error(msg: sass::string("Function " ) + c->name() + " finished without @return" , pstate: c->pstate(), traces); |
1067 | } |
1068 | callee_stack().pop_back(); |
1069 | traces.pop_back(); |
1070 | } |
1071 | |
1072 | // else if it's a user-defined c function |
1073 | // convert call into C-API compatible form |
1074 | else if (c_function) { |
1075 | Sass_Function_Fn c_func = sass_function_get_function(cb: c_function); |
1076 | if (full_name == "*[f]" ) { |
1077 | String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, c->pstate(), c->name()); |
1078 | Arguments_Obj new_args = SASS_MEMORY_NEW(Arguments, c->pstate()); |
1079 | new_args->append(SASS_MEMORY_NEW(Argument, c->pstate(), str)); |
1080 | new_args->concat(v: args); |
1081 | args = new_args; |
1082 | } |
1083 | |
1084 | // populates env with default values for params |
1085 | sass::string ff(c->name()); |
1086 | bind(type: sass::string("Function" ), name: c->name(), params, args, &fn_env, this, traces); |
1087 | sass::string msg(", in function `" + c->name() + "`" ); |
1088 | traces.push_back(x: Backtrace(c->pstate(), msg)); |
1089 | callee_stack().push_back(x: { |
1090 | .name: c->name().c_str(), |
1091 | .path: c->pstate().getPath(), |
1092 | .line: c->pstate().getLine(), |
1093 | .column: c->pstate().getColumn(), |
1094 | .type: SASS_CALLEE_C_FUNCTION, |
1095 | .env: { .frame: env } |
1096 | }); |
1097 | |
1098 | AST2C ast2c; |
1099 | union Sass_Value* c_args = sass_make_list(len: params->length(), sep: SASS_COMMA, is_bracketed: false); |
1100 | for(size_t i = 0; i < params->length(); i++) { |
1101 | Parameter_Obj param = params->at(i); |
1102 | sass::string key = param->name(); |
1103 | AST_Node_Obj node = fn_env.get_local(key); |
1104 | ExpressionObj arg = Cast<Expression>(ptr: node); |
1105 | sass_list_set_value(v: c_args, i, value: arg->perform(op: &ast2c)); |
1106 | } |
1107 | union Sass_Value* c_val = c_func(c_args, c_function, compiler()); |
1108 | if (sass_value_get_tag(v: c_val) == SASS_ERROR) { |
1109 | sass::string message("error in C function " + c->name() + ": " + sass_error_get_message(v: c_val)); |
1110 | sass_delete_value(val: c_val); |
1111 | sass_delete_value(val: c_args); |
1112 | error(msg: message, pstate: c->pstate(), traces); |
1113 | } else if (sass_value_get_tag(v: c_val) == SASS_WARNING) { |
1114 | sass::string message("warning in C function " + c->name() + ": " + sass_warning_get_message(v: c_val)); |
1115 | sass_delete_value(val: c_val); |
1116 | sass_delete_value(val: c_args); |
1117 | error(msg: message, pstate: c->pstate(), traces); |
1118 | } |
1119 | result = c2ast(v: c_val, traces, pstate: c->pstate()); |
1120 | |
1121 | callee_stack().pop_back(); |
1122 | traces.pop_back(); |
1123 | sass_delete_value(val: c_args); |
1124 | if (c_val != c_args) |
1125 | sass_delete_value(val: c_val); |
1126 | } |
1127 | |
1128 | // link back to function definition |
1129 | // only do this for custom functions |
1130 | if (result->pstate().getSrcId() == sass::string::npos) |
1131 | result->pstate(pstate__: c->pstate()); |
1132 | |
1133 | result = result->perform(op: this); |
1134 | result->is_interpolant(is_interpolant__: c->is_interpolant()); |
1135 | env_stack().pop_back(); |
1136 | return result.detach(); |
1137 | } |
1138 | |
1139 | Expression* Eval::operator()(Variable* v) |
1140 | { |
1141 | ExpressionObj value; |
1142 | Env* env = environment(); |
1143 | const sass::string& name(v->name()); |
1144 | EnvResult rv(env->find(key: name)); |
1145 | if (rv.found) value = static_cast<Expression*>(rv.it->second.ptr()); |
1146 | else error(msg: "Undefined variable: \"" + v->name() + "\"." , pstate: v->pstate(), traces); |
1147 | if (Argument* arg = Cast<Argument>(ptr: value)) value = arg->value(); |
1148 | if (Number* nr = Cast<Number>(ptr: value)) nr->zero(zero__: true); // force flag |
1149 | value->is_interpolant(is_interpolant__: v->is_interpolant()); |
1150 | if (force) value->is_expanded(is_expanded__: false); |
1151 | value->set_delayed(false); // verified |
1152 | value = value->perform(op: this); |
1153 | if(!force) rv.it->second = value; |
1154 | return value.detach(); |
1155 | } |
1156 | |
1157 | Expression* Eval::operator()(Color_RGBA* c) |
1158 | { |
1159 | return c; |
1160 | } |
1161 | |
1162 | Expression* Eval::operator()(Color_HSLA* c) |
1163 | { |
1164 | return c; |
1165 | } |
1166 | |
1167 | Expression* Eval::operator()(Number* n) |
1168 | { |
1169 | return n; |
1170 | } |
1171 | |
1172 | Expression* Eval::operator()(Boolean* b) |
1173 | { |
1174 | return b; |
1175 | } |
1176 | |
1177 | void Eval::interpolation(Context& ctx, sass::string& res, ExpressionObj ex, bool into_quotes, bool was_itpl) { |
1178 | |
1179 | bool needs_closing_brace = false; |
1180 | |
1181 | if (Arguments* args = Cast<Arguments>(ptr: ex)) { |
1182 | List* ll = SASS_MEMORY_NEW(List, args->pstate(), 0, SASS_COMMA); |
1183 | for(auto arg : args->elements()) { |
1184 | ll->append(element: arg->value()); |
1185 | } |
1186 | ll->is_interpolant(is_interpolant__: args->is_interpolant()); |
1187 | needs_closing_brace = true; |
1188 | res += "(" ; |
1189 | ex = ll; |
1190 | } |
1191 | if (Number* nr = Cast<Number>(ptr: ex)) { |
1192 | Number reduced(nr); |
1193 | reduced.reduce(); |
1194 | if (!reduced.is_valid_css_unit()) { |
1195 | traces.push_back(x: Backtrace(nr->pstate())); |
1196 | throw Exception::InvalidValue(traces, *nr); |
1197 | } |
1198 | } |
1199 | if (Argument* arg = Cast<Argument>(ptr: ex)) { |
1200 | ex = arg->value(); |
1201 | } |
1202 | if (String_Quoted* sq = Cast<String_Quoted>(ptr: ex)) { |
1203 | if (was_itpl) { |
1204 | bool was_interpolant = ex->is_interpolant(); |
1205 | ex = SASS_MEMORY_NEW(String_Constant, sq->pstate(), sq->value()); |
1206 | ex->is_interpolant(is_interpolant__: was_interpolant); |
1207 | } |
1208 | } |
1209 | |
1210 | if (Cast<Null>(ptr: ex)) { return; } |
1211 | |
1212 | // parent selector needs another go |
1213 | if (Cast<Parent_Reference>(ptr: ex)) { |
1214 | // XXX: this is never hit via spec tests |
1215 | ex = ex->perform(op: this); |
1216 | } |
1217 | |
1218 | if (List* l = Cast<List>(ptr: ex)) { |
1219 | List_Obj ll = SASS_MEMORY_NEW(List, l->pstate(), 0, l->separator()); |
1220 | // this fixes an issue with bourbon sample, not really sure why |
1221 | // if (l->size() && Cast<Null>((*l)[0])) { res += ""; } |
1222 | for(ExpressionObj item : *l) { |
1223 | item->is_interpolant(is_interpolant__: l->is_interpolant()); |
1224 | sass::string rl("" ); interpolation(ctx, res&: rl, ex: item, into_quotes, was_itpl: l->is_interpolant()); |
1225 | bool is_null = Cast<Null>(ptr: item) != 0; // rl != "" |
1226 | if (!is_null) ll->append(SASS_MEMORY_NEW(String_Quoted, item->pstate(), rl)); |
1227 | } |
1228 | // Check indicates that we probably should not get a list |
1229 | // here. Normally single list items are already unwrapped. |
1230 | if (l->size() > 1) { |
1231 | // string_to_output would fail "#{'_\a' '_\a'}"; |
1232 | sass::string str(ll->to_string(opt: options())); |
1233 | str = read_hex_escapes(str); // read escapes |
1234 | newline_to_space(str); // replace directly |
1235 | res += str; // append to result string |
1236 | } else { |
1237 | res += (ll->to_string(opt: options())); |
1238 | } |
1239 | ll->is_interpolant(is_interpolant__: l->is_interpolant()); |
1240 | } |
1241 | |
1242 | // Value |
1243 | // Function_Call |
1244 | // Selector_List |
1245 | // String_Quoted |
1246 | // String_Constant |
1247 | // Binary_Expression |
1248 | else { |
1249 | // ex = ex->perform(this); |
1250 | if (into_quotes && ex->is_interpolant()) { |
1251 | res += evacuate_escapes(str: ex ? ex->to_string(opt: options()) : "" ); |
1252 | } else { |
1253 | sass::string str(ex ? ex->to_string(opt: options()) : "" ); |
1254 | if (into_quotes) str = read_hex_escapes(str); |
1255 | res += str; // append to result string |
1256 | } |
1257 | } |
1258 | |
1259 | if (needs_closing_brace) res += ")" ; |
1260 | |
1261 | } |
1262 | |
1263 | Expression* Eval::operator()(String_Schema* s) |
1264 | { |
1265 | size_t L = s->length(); |
1266 | bool into_quotes = false; |
1267 | if (L > 1) { |
1268 | if (!Cast<String_Quoted>(ptr: (*s)[0]) && !Cast<String_Quoted>(ptr: (*s)[L - 1])) { |
1269 | if (String_Constant* l = Cast<String_Constant>(ptr: (*s)[0])) { |
1270 | if (String_Constant* r = Cast<String_Constant>(ptr: (*s)[L - 1])) { |
1271 | if (r->value().size() > 0) { |
1272 | if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true; |
1273 | if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true; |
1274 | } |
1275 | } |
1276 | } |
1277 | } |
1278 | } |
1279 | bool was_quoted = false; |
1280 | bool was_interpolant = false; |
1281 | sass::string res("" ); |
1282 | for (size_t i = 0; i < L; ++i) { |
1283 | bool is_quoted = Cast<String_Quoted>(ptr: (*s)[i]) != NULL; |
1284 | if (was_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " " ; } |
1285 | else if (i > 0 && is_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " " ; } |
1286 | ExpressionObj ex = (*s)[i]->perform(op: this); |
1287 | interpolation(ctx, res, ex, into_quotes, was_itpl: ex->is_interpolant()); |
1288 | was_quoted = Cast<String_Quoted>(ptr: (*s)[i]) != NULL; |
1289 | was_interpolant = (*s)[i]->is_interpolant(); |
1290 | |
1291 | } |
1292 | if (!s->is_interpolant()) { |
1293 | if (s->length() > 1 && res == "" ) return SASS_MEMORY_NEW(Null, s->pstate()); |
1294 | String_Constant_Obj str = SASS_MEMORY_NEW(String_Constant, s->pstate(), res, s->css()); |
1295 | return str.detach(); |
1296 | } |
1297 | // string schema seems to have a special unquoting behavior (also handles "nested" quotes) |
1298 | String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), res, 0, false, false, false, s->css()); |
1299 | // if (s->is_interpolant()) str->quote_mark(0); |
1300 | // String_Constant* str = SASS_MEMORY_NEW(String_Constant, s->pstate(), res); |
1301 | if (str->quote_mark()) str->quote_mark(quote_mark__: '*'); |
1302 | else if (!is_in_comment) str->value(value__: string_to_output(str: str->value())); |
1303 | str->is_interpolant(is_interpolant__: s->is_interpolant()); |
1304 | return str.detach(); |
1305 | } |
1306 | |
1307 | |
1308 | Expression* Eval::operator()(String_Constant* s) |
1309 | { |
1310 | return s; |
1311 | } |
1312 | |
1313 | Expression* Eval::operator()(String_Quoted* s) |
1314 | { |
1315 | String_Quoted* str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), "" ); |
1316 | str->value(value__: s->value()); |
1317 | str->quote_mark(quote_mark__: s->quote_mark()); |
1318 | str->is_interpolant(is_interpolant__: s->is_interpolant()); |
1319 | return str; |
1320 | } |
1321 | |
1322 | Expression* Eval::operator()(SupportsOperation* c) |
1323 | { |
1324 | Expression* left = c->left()->perform(op: this); |
1325 | Expression* right = c->right()->perform(op: this); |
1326 | SupportsOperation* cc = SASS_MEMORY_NEW(SupportsOperation, |
1327 | c->pstate(), |
1328 | Cast<SupportsCondition>(left), |
1329 | Cast<SupportsCondition>(right), |
1330 | c->operand()); |
1331 | return cc; |
1332 | } |
1333 | |
1334 | Expression* Eval::operator()(SupportsNegation* c) |
1335 | { |
1336 | Expression* condition = c->condition()->perform(op: this); |
1337 | SupportsNegation* cc = SASS_MEMORY_NEW(SupportsNegation, |
1338 | c->pstate(), |
1339 | Cast<SupportsCondition>(condition)); |
1340 | return cc; |
1341 | } |
1342 | |
1343 | Expression* Eval::operator()(SupportsDeclaration* c) |
1344 | { |
1345 | Expression* feature = c->feature()->perform(op: this); |
1346 | Expression* value = c->value()->perform(op: this); |
1347 | SupportsDeclaration* cc = SASS_MEMORY_NEW(SupportsDeclaration, |
1348 | c->pstate(), |
1349 | feature, |
1350 | value); |
1351 | return cc; |
1352 | } |
1353 | |
1354 | Expression* Eval::operator()(Supports_Interpolation* c) |
1355 | { |
1356 | Expression* value = c->value()->perform(op: this); |
1357 | Supports_Interpolation* cc = SASS_MEMORY_NEW(Supports_Interpolation, |
1358 | c->pstate(), |
1359 | value); |
1360 | return cc; |
1361 | } |
1362 | |
1363 | Expression* Eval::operator()(At_Root_Query* e) |
1364 | { |
1365 | ExpressionObj feature = e->feature(); |
1366 | feature = (feature ? feature->perform(op: this) : 0); |
1367 | ExpressionObj value = e->value(); |
1368 | value = (value ? value->perform(op: this) : 0); |
1369 | Expression* ee = SASS_MEMORY_NEW(At_Root_Query, |
1370 | e->pstate(), |
1371 | Cast<String>(feature), |
1372 | value); |
1373 | return ee; |
1374 | } |
1375 | |
1376 | Media_Query* Eval::operator()(Media_Query* q) |
1377 | { |
1378 | String_Obj t = q->media_type(); |
1379 | t = static_cast<String*>(t.isNull() ? 0 : t->perform(op: this)); |
1380 | Media_Query_Obj qq = SASS_MEMORY_NEW(Media_Query, |
1381 | q->pstate(), |
1382 | t, |
1383 | q->length(), |
1384 | q->is_negated(), |
1385 | q->is_restricted()); |
1386 | for (size_t i = 0, L = q->length(); i < L; ++i) { |
1387 | qq->append(element: static_cast<Media_Query_Expression*>((*q)[i]->perform(op: this))); |
1388 | } |
1389 | return qq.detach(); |
1390 | } |
1391 | |
1392 | Expression* Eval::operator()(Media_Query_Expression* e) |
1393 | { |
1394 | ExpressionObj feature = e->feature(); |
1395 | feature = (feature ? feature->perform(op: this) : 0); |
1396 | if (feature && Cast<String_Quoted>(ptr: feature)) { |
1397 | feature = SASS_MEMORY_NEW(String_Quoted, |
1398 | feature->pstate(), |
1399 | Cast<String_Quoted>(feature)->value()); |
1400 | } |
1401 | ExpressionObj value = e->value(); |
1402 | value = (value ? value->perform(op: this) : 0); |
1403 | if (value && Cast<String_Quoted>(ptr: value)) { |
1404 | // XXX: this is never hit via spec tests |
1405 | value = SASS_MEMORY_NEW(String_Quoted, |
1406 | value->pstate(), |
1407 | Cast<String_Quoted>(value)->value()); |
1408 | } |
1409 | return SASS_MEMORY_NEW(Media_Query_Expression, |
1410 | e->pstate(), |
1411 | feature, |
1412 | value, |
1413 | e->is_interpolated()); |
1414 | } |
1415 | |
1416 | Expression* Eval::operator()(Null* n) |
1417 | { |
1418 | return n; |
1419 | } |
1420 | |
1421 | Expression* Eval::operator()(Argument* a) |
1422 | { |
1423 | ExpressionObj val = a->value()->perform(op: this); |
1424 | bool is_rest_argument = a->is_rest_argument(); |
1425 | bool is_keyword_argument = a->is_keyword_argument(); |
1426 | |
1427 | if (a->is_rest_argument()) { |
1428 | if (val->concrete_type() == Expression::MAP) { |
1429 | is_rest_argument = false; |
1430 | is_keyword_argument = true; |
1431 | } |
1432 | else if(val->concrete_type() != Expression::LIST) { |
1433 | List_Obj wrapper = SASS_MEMORY_NEW(List, |
1434 | val->pstate(), |
1435 | 0, |
1436 | SASS_COMMA, |
1437 | true); |
1438 | wrapper->append(element: val); |
1439 | val = wrapper; |
1440 | } |
1441 | } |
1442 | return SASS_MEMORY_NEW(Argument, |
1443 | a->pstate(), |
1444 | val, |
1445 | a->name(), |
1446 | is_rest_argument, |
1447 | is_keyword_argument); |
1448 | } |
1449 | |
1450 | Expression* Eval::operator()(Arguments* a) |
1451 | { |
1452 | Arguments_Obj aa = SASS_MEMORY_NEW(Arguments, a->pstate()); |
1453 | if (a->length() == 0) return aa.detach(); |
1454 | for (size_t i = 0, L = a->length(); i < L; ++i) { |
1455 | ExpressionObj rv = (*a)[i]->perform(op: this); |
1456 | Argument* arg = Cast<Argument>(ptr: rv); |
1457 | if (!(arg->is_rest_argument() || arg->is_keyword_argument())) { |
1458 | aa->append(element: arg); |
1459 | } |
1460 | } |
1461 | |
1462 | if (a->has_rest_argument()) { |
1463 | ExpressionObj rest = a->get_rest_argument()->perform(op: this); |
1464 | ExpressionObj splat = Cast<Argument>(ptr: rest)->value()->perform(op: this); |
1465 | |
1466 | Sass_Separator separator = SASS_COMMA; |
1467 | List* ls = Cast<List>(ptr: splat); |
1468 | Map* ms = Cast<Map>(ptr: splat); |
1469 | |
1470 | List_Obj arglist = SASS_MEMORY_NEW(List, |
1471 | splat->pstate(), |
1472 | 0, |
1473 | ls ? ls->separator() : separator, |
1474 | true); |
1475 | |
1476 | if (ls && ls->is_arglist()) { |
1477 | arglist->concat(v: ls); |
1478 | } else if (ms) { |
1479 | aa->append(SASS_MEMORY_NEW(Argument, splat->pstate(), ms, "" , false, true)); |
1480 | } else if (ls) { |
1481 | arglist->concat(v: ls); |
1482 | } else { |
1483 | arglist->append(element: splat); |
1484 | } |
1485 | if (arglist->length()) { |
1486 | aa->append(SASS_MEMORY_NEW(Argument, splat->pstate(), arglist, "" , true)); |
1487 | } |
1488 | } |
1489 | |
1490 | if (a->has_keyword_argument()) { |
1491 | ExpressionObj rv = a->get_keyword_argument()->perform(op: this); |
1492 | Argument* rvarg = Cast<Argument>(ptr: rv); |
1493 | ExpressionObj kwarg = rvarg->value()->perform(op: this); |
1494 | |
1495 | aa->append(SASS_MEMORY_NEW(Argument, kwarg->pstate(), kwarg, "" , false, true)); |
1496 | } |
1497 | return aa.detach(); |
1498 | } |
1499 | |
1500 | Expression* Eval::(Comment* c) |
1501 | { |
1502 | return 0; |
1503 | } |
1504 | |
1505 | SelectorList* Eval::operator()(Selector_Schema* s) |
1506 | { |
1507 | LOCAL_FLAG(is_in_selector_schema, true); |
1508 | // the parser will look for a brace to end the selector |
1509 | ExpressionObj sel = s->contents()->perform(op: this); |
1510 | sass::string result_str(sel->to_string(opt: options())); |
1511 | result_str = unquote(Util::rtrim(str: result_str)); |
1512 | ItplFile* source = SASS_MEMORY_NEW(ItplFile, |
1513 | result_str.c_str(), s->pstate()); |
1514 | Parser p(source, ctx, traces); |
1515 | |
1516 | // If a schema contains a reference to parent it is already |
1517 | // connected to it, so don't connect implicitly anymore |
1518 | SelectorListObj parsed = p.parseSelectorList(chroot: true); |
1519 | flag_is_in_selector_schema.reset(); |
1520 | return parsed.detach(); |
1521 | } |
1522 | |
1523 | Expression* Eval::operator()(Parent_Reference* p) |
1524 | { |
1525 | if (SelectorListObj pr = exp.original()) { |
1526 | return operator()(pr); |
1527 | } else { |
1528 | return SASS_MEMORY_NEW(Null, p->pstate()); |
1529 | } |
1530 | } |
1531 | |
1532 | SimpleSelector* Eval::operator()(SimpleSelector* s) |
1533 | { |
1534 | return s; |
1535 | } |
1536 | |
1537 | PseudoSelector* Eval::operator()(PseudoSelector* pseudo) |
1538 | { |
1539 | // ToDo: should we eval selector? |
1540 | return pseudo; |
1541 | }; |
1542 | |
1543 | } |
1544 | |